Anuncios

Bienvenidos sean a este post, en el post anterior terminamos con nuestro segundo proyecto y en este post veremos todos los archivos con sus respectivos codigos finales para poder generar el juego.

Anuncios

En caso de necesitar una explicacion del alguno de los codigos les sugiero visitar sus correspondientes posts, aqui el listado de ellos:

Anuncios

En estos posts encontraran muchas de las explicaciones de lo realizado en los distintos codigos, a continuacion comenzaremos a detallar cada uno de ellos pero antes veamos cual es el modelo a elegir para diseñarlo:

  • Dispositivos: Phone and Tablet
  • Actividad: Empty Activity
  • Nombre: Pong
  • Nombre de paquete: org.example.pong
  • API Minimo: API 14 (Android 4.0)
Anuncios

Les dejo el link para descargar los sonidos que utilizamos en la creacion del juego:

Anuncios

PongActivity.java

package org.example.pong;

import android.app.Activity;
import android.graphics.Point;
import android.os.Bundle;
import android.view.Display;

public class PongActivity extends Activity {

    private PongJuego mPongJuego;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Display display = getWindowManager().getDefaultDisplay();
        Point tamano = new Point();
        display.getSize(tamano);

        mPongJuego = new PongJuego(this, tamano.x, tamano.y);
        setContentView(mPongJuego);
    }

    @Override
    protected void onResume(){
        super.onResume();
        mPongJuego.resume();
    }

    @Override
    protected void onPause(){
        super.onPause();
        mPongJuego.pause();
    }
}
Anuncios
Nota: Para esta clase deben renombrar a MainActivity como PongActivity por medio de Refactor->Rename
Anuncios

PongJuego.java

package org.example.pong;

import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Build;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Toast;

import java.io.IOException;

class PongJuego extends SurfaceView implements Runnable {

    private final boolean DEPURANDO = true;

    private SurfaceHolder mNuestroCont;
    private Canvas mCanvas;
    private Paint mPincel;

    private long mFPS;

    private final int MILES_EN_SEGUNDO = 1000;

    private int mScreenX;
    private int mScreenY;

    private int mFontTam;
    private int mFontMargen;

    private Bate mBate;
    private Pelota mPelota;

    private int mPuntaje;
    private int mVidas;

    private Thread mJuegoThread = null;
    private volatile boolean mJugando;
    private boolean mPausado = true;

    private SoundPool mSP;
    private int mBeepID = -1;
    private int mBoopID = -1;
    private int mBopID = -1;
    private int mMissID = -1;

    public PongJuego(Context contexto, int x, int y){
        super(contexto);

        mScreenX = x;
        mScreenY = y;

        mFontTam = mScreenX / 20;
        mFontMargen = mScreenX /75;

        mNuestroCont = getHolder();
        mPincel = new Paint();

        mPelota = new Pelota(mScreenX);
        mBate = new Bate(mScreenX,mScreenY);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
            AudioAttributes atributos = new AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_MEDIA)
                    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                    .build();
            mSP = new SoundPool.Builder()
                    .setMaxStreams(5)
                    .setAudioAttributes(atributos)
                    .build();

        } else {
            mSP = new SoundPool(5, AudioManager.STREAM_MUSIC,0);
        }

        try{

            AssetManager assetManager = contexto.getAssets();
            AssetFileDescriptor descriptor;

            descriptor = assetManager.openFd("beep.wav");
            mBeepID = mSP.load(contexto,R.raw.beep,1);

            descriptor = assetManager.openFd("boop.wav");
            mBoopID = mSP.load(contexto,R.raw.boop,1);

            descriptor = assetManager.openFd("bop.wav");
            mBopID = mSP.load(contexto,R.raw.bop,1);

            descriptor = assetManager.openFd("miss.wav");
            mMissID = mSP.load(contexto,R.raw.miss,1);

        } catch (IOException e){
            Log.e("Error","Fallo la carga de alguno de los archivos.");
        }
        iniciaNuevoJuego();
    }

    private void iniciaNuevoJuego(){

        mPuntaje = 0;
        mVidas = 3;
        mPelota.reset(mScreenX,mScreenY);
    }

    private void dibujar(){
        if (mNuestroCont.getSurface().isValid()){
            mCanvas = mNuestroCont.lockCanvas();
            mCanvas.drawColor(Color.argb(255,26,128,182));
            mPincel.setColor(Color.argb(255,255,255,255));
            mCanvas.drawRect(mPelota.getmRect(),mPincel);
            mCanvas.drawRect(mBate.getmRect(),mPincel);
            mPincel.setTextSize(mFontTam);
            mCanvas.drawText("Puntaje: " + mPuntaje
                            + "  Vidas: " + mVidas,
                    mFontMargen,mFontTam,mPincel);
            if (DEPURANDO){
                imprimirDepuracion();
            }
            mNuestroCont.unlockCanvasAndPost(mCanvas);
        }
    }

    private void imprimirDepuracion(){
        int debugTam = mFontTam / 2;
        int debugComienzo = 150;
        mPincel.setTextSize(debugTam);
        mCanvas.drawText("FPS: " + mFPS,
                10,
                debugComienzo + debugTam,
                mPincel);
    }

    @Override
    public void run(){
        while(mJugando){
            long frameInicio = System.currentTimeMillis();
            if (!mPausado){
                actualizar();
                detectarColisiones();
            }
            dibujar();
            long esteFrameTiempo = System.currentTimeMillis() - frameInicio;
            if (esteFrameTiempo > 0){
                mFPS = MILES_EN_SEGUNDO / esteFrameTiempo;
            }
        }
    }

    private void actualizar(){
        mPelota.actualizar(mFPS);
        mBate.actualizar(mFPS);
    }

    private void detectarColisiones(){
        if(RectF.intersects(mBate.getmRect(),mPelota.getmRect())){
            mPelota.reboteBate(mBate.getmRect());
            mPelota.incremetarVelocidad();
            mPuntaje++;
            mSP.play(mBeepID,1,1,0,0,1);
        }

        if(mPelota.getmRect().bottom >= mScreenY){
            mPelota.invertirYVelocidad();
            mVidas--;
            mSP.play(mMissID,1,1,0,0,1);
            if(mVidas==0){
                mPausado=true;
                iniciaNuevoJuego();
            }
        }

        if (mPelota.getmRect().top <= 0){
            mPelota.invertirYVelocidad();
            mSP.play(mBoopID,1,1,0,0,1);
        }

        if (mPelota.getmRect().left <= 0){
            mPelota.invertirXVelocidad();
            mSP.play(mBopID,1,1,0,0,1);
        }

        if (mPelota.getmRect().right >= mScreenX){
            mPelota.invertirXVelocidad();
            mSP.play(mBopID,1,1,0,0,1);
        }
    }

    public void pause(){
        mJugando = false;
        try{
            mJuegoThread.join();
        } catch (InterruptedException e){
            Log.e("Error","uniendo a thread");
        }
    }

    public void resume(){
        mJugando = true;
        mJuegoThread = new Thread(this);
        mJuegoThread.start();
    }

    @Override
    public boolean onTouchEvent(MotionEvent motionEvent){
        switch (motionEvent.getAction()
                & MotionEvent.ACTION_MASK){
            case MotionEvent.ACTION_DOWN:
                mPausado = false;
                if(motionEvent.getX() > (mScreenX/2)){
                    mBate.setEstadoMovimiento(mBate.DERECHA);
                } else {
                    mBate.setEstadoMovimiento(mBate.IZQUIERDA);
                }
                break;
            case MotionEvent.ACTION_UP:
                mBate.setEstadoMovimiento(mBate.DETENIDO);
                break;
        }
        return true;
    }

}
Anuncios

Bate.java

package org.example.pong;

import android.graphics.RectF;

class Bate {
    private RectF mRect;
    private float mLongitud;
    private float mXCoord;
    private float mBateVeloc;
    private int mScreenX;

    final int DETENIDO = 0;
    final int IZQUIERDA = 1;
    final int DERECHA = 2;

    private int mBateMueve = DETENIDO;

    Bate(int sx, int sy){
        mScreenX = sx;
        mLongitud = mScreenX / 8;
        float altura = sy / 40;
        mXCoord = mScreenX / 2;
        float mYCoord = sy - altura;
        mRect = new RectF(mXCoord, mYCoord,
                mXCoord + mLongitud,
                mYCoord + altura);
        mBateVeloc = mScreenX;
    }

    RectF getmRect(){
        return mRect;
    }

    void setEstadoMovimiento(int state){
        mBateMueve = state;
    }

    void actualizar(long fps){
        if(mBateMueve == IZQUIERDA){
            mXCoord = mXCoord - (mBateVeloc/fps);
        }
        if (mBateMueve == DERECHA){
            mXCoord = mXCoord + (mBateVeloc/fps);
        }
        if(mXCoord < 0){
            mXCoord = 0;
        }
        else if (mXCoord + mLongitud > mScreenX){
            mXCoord = mScreenX - mLongitud;
        }

        mRect.left = mXCoord;
        mRect.right = mXCoord + mLongitud;
    }
}
Anuncios

Pelota.java

package org.example.pong;

import android.graphics.RectF;

class Pelota {
    private RectF mRect;
    private float mXVelocidad;
    private float mYVelocidad;
    private float mPelotaAncho;
    private float mPelotaAlto;

    Pelota(int screenX){
        mPelotaAlto = screenX / 100;
        mPelotaAncho = screenX / 100;
        mRect = new RectF();
    }

    RectF getmRect(){
        return mRect;
    }

    void actualizar(long fps){
        mRect.left = mRect.left + (mXVelocidad / fps);
        mRect.top = mRect.top + (mYVelocidad / fps);
        mRect.right = mRect.left + mPelotaAncho;
        mRect.bottom = mRect.top + mPelotaAlto;
    }

    void invertirYVelocidad(){
        mYVelocidad = -mYVelocidad;
    }

    void invertirXVelocidad(){
        mXVelocidad = -mXVelocidad;
    }

    void reset(int x, int y){
        mRect.left = x / 2;
        mRect.top = 0;
        mRect.right = (x / 2) + mPelotaAncho;
        mRect.bottom = mPelotaAlto;

        mYVelocidad = -(y/3);
        mXVelocidad = y / 3;
    }

    void incremetarVelocidad(){
        mXVelocidad = mXVelocidad * 1.1f;
        mYVelocidad = mYVelocidad * 1.1f;
    }

    void reboteBate(RectF posicionBate){

        float bateCentro = posicionBate.left + (posicionBate.width()/2);
        float pelotaCentro = mRect.left + (mPelotaAncho / 2);
        float interseccRelativa = (bateCentro - pelotaCentro);
        if (interseccRelativa < 0){
            mXVelocidad = Math.abs(mXVelocidad);
        } else {
            mXVelocidad = -Math.abs(mXVelocidad);
        }
        invertirYVelocidad();
    }
}
Anuncios

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.example.pong">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".PongActivity"
            android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
            android:screenOrientation="landscape" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
Anuncios

Con todo esto ya podran generar el juego como se ve en el siguiente video

Anuncios

Este es un juego muy util para aprender varios conceptos complementarios con respecto al primero, entre ellos:

  • Como implementar un game engine
  • Como funciona la deteccion de colisiones
  • Como agregar sonidos a nuestro juego
  • Como manejar (de manera muy simple) un sprite en pantalla
Anuncios

Espero les haya gustado la realizacion de este segundo juego y como podran observar a medida que avanzamos vamos mejorando nuestras herramientas y conocimientos para poder lograr verdaderos juegos simples pero entretenidos y poder cada dia estar mas cerca de poder, para aquellos que lo sueñan, crear nuestros propios juegos, ya saben que pueden seguirme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios

Tengo un Patreon donde podes acceder de manera exclusiva a material para este blog antes de ser publicado, sigue los pasos del link para saber como.

Tambien podes donar

Es para mantenimiento del sitio, gracias!

$1.00