Skip to content

Instantly share code, notes, and snippets.

@arteymix
Created February 15, 2012 00:10
Show Gist options
  • Save arteymix/1831926 to your computer and use it in GitHub Desktop.
Save arteymix/1831926 to your computer and use it in GitHub Desktop.
Abstract class used to provide Android activities with a physic and draw engine
/*
* This file is part of Funky Domino.
*
* Funky Domino is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Funky Domino is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Funky Domino. If not, see <http://www.gnu.org/licenses/>.
*/
package com.gmxteam.funkydomino.activities;
// Importations locales
import com.gmxteam.funkydomino.graphicals.components.Component;
import com.gmxteam.funkydomino.graphicals.widgets.Widget;
// Importations pour le moteur de collisions
import org.jbox2d.collision.AABB;
import org.jbox2d.collision.Shape;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.World;
// Importations pour le moteur de rendu
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
// Librairies standard Android
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Handler;
import android.view.Window;
import android.view.WindowManager;
import android.view.MotionEvent;
import android.view.View;
// Librairie standard Java
import java.util.ArrayList;
/**
* Classe abstraite permettant une implémentation efficace d'un interface JBox2D.
* Funky Domino sera premièrement développé en canvas afin d'obtenir rapidement
* des résultats. Il sera ensuite converti en OpenGL afin d'en améliorer
* considérablement les performances.
*
* Le fonctionnement est simple. On redéfinit une activité android en y intégrant
* un moteur de physique et de rendu. On intègre aussi certaines interactions
* avec l'utilisateur pour minimiser le code des activités.
*
* Les éléments d'interfaces qui seront alors utilisés pourront être ceux de la
* librairie standard, mais il est recommandé d'utiliser des élément de physique
* afin de nous donner plus de liberté. Un menu animé par la physique, c'est pas
* cool ça?
*
* Le redessinage est géré dans le thread de l'utilisateur interface. Quand on
* finit de redessiner une image, elle est automatiquement invalidée et quand le
* thread UI sera prêt à la redessiner, le processus recommencera.
*
* La physique est gérée dans un thread à part.
* @author Guillaume Poirier-Morency
*/
public abstract class JBox2DCanvasActivity extends Activity {
////////////////////////////////////////////////////////////////////////////
// Variables d'environnement et de débogage
private final boolean IS_DEBUG_ENABLED = true;
private long renderingTime = 0;
private long drawnWidgets = 0;
private long drawnComponents = 0;
private long numberOfDrawingLoopsDone = 0;
private long numberOfPhysicsLoopsDone = 0;
private long fps;
private boolean isPaused = false;
////////////////////////////////////////////////////////////////////////////
/**
* On dessine sur une surface OpenGL ES 2.0.
*/
private View canvasView;
////////////////////////////////////////////////////////////////////////////
// Variables pour le moteur de collisions
/**
*
*/
public World world;
private int sleepTime = 15;
private int iterations = 5;
private AABB worldAABB;
private Handler mHandler;
private Runnable update = new Runnable() {
public void run() {
long timeBefore = System.currentTimeMillis();
/* On fait avancer le temps dans le monde physique du nombre de
* secondes que le dernier calcul a prit. Ainsi, le monde physique
* évolue en temps réel. On rajoute un délai de 15 millisecondes
* afin de ne pas trop surcharger le programme (en particuler le
* rendu).
*/
world.step((float) ((renderingTime + sleepTime) / 1000.0f), iterations);
numberOfPhysicsLoopsDone++;
canvasView.invalidate();
renderingTime = System.currentTimeMillis() - timeBefore;
mHandler.postDelayed(update, sleepTime);
// TODO Corriger l'influence du temps de rendu
}
};
////////////////////////////////////////////////////////////////////////////
// Outils de conversion
/**
* Transforme une valeur en pixels en mètres. Prend en considération les
* dimensions de l'écran et le ratio hauteur par largeur.
* Il inverse aussi les valeurs en y afin de faire correspondre les différents
* systèmes d'axes.
* @param meter est une valeur en mètres.
* @return une valeur en pixels.
*/
public static Vec2 toPixel(Vec2 meter) {
return new Vec2(meter.x, meter.y);
}
/**
* Transforme une valeur en mètres en pixels. Prend en considération les
* dimensions de l'écran et le ratio hauteur par largeur.
* Il inverse aussi les valeurs en y afin de faire correspondre les différents
* systèmes d'axes.
* @param pixel est une valeur en pixels.
* @return une valeur en pixels.
*/
public static Vec2 toMeter(Vec2 pixel) {
return new Vec2(pixel.x, pixel.y);
}
////////////////////////////////////////////////////////////////////////////
/**
* Initialisation du rendu 2D.
*/
private void init() {
// On met l'orientation de l'appareil en mode paysage.
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
// On cache le menu
requestWindowFeature(Window.FEATURE_NO_TITLE);
// On mets l'application en plein écran
final Window window = getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
// TODO Créer un ContentView pour l'image de chargement.
canvasView = new View(this) {
@Override
public void onDraw(Canvas c) {
if (!isPaused) {
long timeInit = System.currentTimeMillis();
onDrawFrame(c);
numberOfDrawingLoopsDone++;
drawnComponents = 0;
drawnWidgets = 0;
fps = (1000 / (System.currentTimeMillis() - timeInit));
}
}
};
// On configure le moteur de physique
worldAABB = new AABB();
// TODO Définir les dimensions du monde en fonction du ratio de la taille de l'écran.
worldAABB.lowerBound.set(new Vec2(0.0f, 0.0f));
worldAABB.upperBound.set(new Vec2(800.0f, 480.0f ));
// On ajoute la gravité et le worldAABB dans world
world = new World(worldAABB, new Vec2(0.0f, -9.8f), false);
// On démarre le Thread qui va gérer le moteur de physique
mHandler = new Handler();
mHandler.post(update);
// On définit la surface 2D comme surface de dessin
setContentView(canvasView);
}
////////////////////////////////////////////////////////////////////////////
// Contrôleurs
/**
*
* @param savedInstanceState
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
init();
}
/**
* Méthode appelé quand le programme se met en pause.
*/
@Override
protected void onPause() {
super.onPause();
isPaused = true;
// On vide le handler.
mHandler = new Handler();
}
/**
* Méthode appelée quand le programme est en pause et qu'il se fait
* réveiller.
*/
@Override
protected void onResume() {
super.onResume();
isPaused = false;
// On redémarre le handler.
mHandler.post(update);
// On réveille le redraw du canvas
canvasView.postInvalidate();
}
////////////////////////////////////////////////////////////////////////////
// User input handling
/**
* On récupère le MotionEvent et on le redirige vers tous les objets graphiques
* avec qui on trouve une collision.
* @param me est le MotionEvent qui doit être géré.
* @return toujours true...
*/
@Override
public boolean onTouchEvent(MotionEvent me) {
// TODO Mettre le MotionEvent dans le AABB.
AABB areaAABB = new AABB();
// On récupère la liste des shapes dans la zone touchée.
Shape[] shapeList = world.query(areaAABB, 500);
// Si la zone contient des widgets ou composants, on les avertit.
for (Shape clickedShape : shapeList) {
if (clickedShape.getBody().getUserData() instanceof Widget) {
((Widget) clickedShape.getBody().getUserData()).onClick(me);
} else if (clickedShape.getBody().getUserData() instanceof Component) {
((Component) clickedShape.getBody().getUserData()).onClick(me);
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////
// Handler pour le rendu 2D (se fait automatiquement)
/**
* Méthode appelée quand la surface est dessinée.
* @param canvas est la surface sur laquelle la surface sera dessinée.
*/
private void onDrawFrame(Canvas canvas) {
drawBackground(canvas);
Body b = this.world.getBodyList();
ArrayList<Widget> drawWidgetLast = new ArrayList<Widget>();
do {
// Draw the body
if (b.getUserData() instanceof Widget) {
drawWidgetLast.add((Widget) b.getUserData());
} else if (b.getUserData() instanceof Component) {
Component c = (Component) b.getUserData();
drawnComponents++;
c.drawCanvas(canvas);
} else {
// Crap.
// throw new UnknownGraphicalElementException();
}
} while ((b = b.getNext()) != null);
for (Widget w : drawWidgetLast) {
drawnWidgets++;
w.drawCanvas(canvas);
}
if (IS_DEBUG_ENABLED) {
drawDebug(canvas);
}
}
////////////////////////////////////////////////////////////////////////////
// Méthode de dessinage
/**
* Dessine l'arrière-plan.
* @param c est le canvas sur lequel l'arrière-plan sera dessiné.
*/
private void drawBackground(Canvas c) {
c.drawColor(Color.WHITE);
}
/**
*
*/
private final Paint DEBUG_PAINT = new Paint();
/**
* Dessine les variables d'environnement.
* @param c est le canvas sur lequel l'les variables de débogage seront
* dessinées.
*/
private void drawDebug(Canvas c) {
float initP = 10.0f;
c.drawText("Nombre de corps dessinés : " + world.getBodyCount() + " corps", 15.0f, initP += 15.0f, DEBUG_PAINT);
c.drawText("Widgets dessinés : " + drawnWidgets + " widgets", 15.0f, initP += 15.0f, DEBUG_PAINT);
c.drawText("Composants dessinés : " + drawnComponents + " composants" + "", 15.0f, initP += 15.0f, DEBUG_PAINT);
c.drawText("Autres corps dessinés : " + (world.getBodyCount() - drawnComponents - drawnWidgets) + " composants" + "", 15.0f, initP += 15.0f, DEBUG_PAINT);
c.drawText("Gravité : " + world.getGravity() + " m/s^2", 15.0f, initP += 15.0f, DEBUG_PAINT);
c.drawText("Temps du rendu : " + renderingTime + " ms", 15.0f, initP += 15.0f, DEBUG_PAINT);
c.drawText("Vecteurs pour les dimensions de l'écran : " + world.getWorldAABB().lowerBound + " et " + world.getWorldAABB().upperBound, 15.0f, initP += 15.0f, DEBUG_PAINT);
c.drawText("Nombre de mises à jour du moteur de physique : " + numberOfPhysicsLoopsDone + " calculs", 15.0f, initP += 15.0f, DEBUG_PAINT);
c.drawText("Nombre de mises à jour du rendu : " + numberOfDrawingLoopsDone, 15.0f, initP += 15.0f, DEBUG_PAINT);
c.drawText("Frame per second : " + fps + " fps", 15.0f, initP += 15.0f, DEBUG_PAINT);
drawActivityDebug(c, initP, DEBUG_PAINT);
}
////////////////////////////////////////////////////////////////////////////
/**
*
* @param c
* @param initP
* @param p
*/
abstract void drawActivityDebug(Canvas c, float initP, Paint p);
}
@arteymix
Copy link
Author

The project is hosted on svn : http://code.google.com/p/funky-domino/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment