Friday, March 25, 2011

AndEngine from Scratch (IV)

Previously on AndEngine from Scratch



Today goals

  • Create the player sprite.
  • Create a Detector to the catapult effect.



Create the player


To create the player i decided to use an AnimatedSprite, but we subclass the AnimatedSprite to be able to personalize it.


To crate the class, go to the folder with the sources in Eclipse. File New -> Class and the Name is Player and the SuperClass is AnimatedSprite. We add a debug TAG and for now is ok.


  1. package com.pruebas.andengine;
  2.  
  3. import org.anddev.andengine.entity.sprite.AnimatedSprite;
  4. import org.anddev.andengine.input.touch.detector.HoldDetector;
  5. import org.anddev.andengine.input.touch.detector.HoldDetector.IHoldDetectorListener;
  6. import org.anddev.andengine.opengl.texture.region.TiledTextureRegion;
  7.  
  8. import android.util.Log;
  9.  
  10. public class Player extends AnimatedSprite  {
  11.  
  12. private static final String TAG = "Player"; 
  13.  
  14. public Player(float pX, float pY, TiledTextureRegion pTiledTextureRegion) {
  15. super(pX, pY, pTiledTextureRegion);  
  16. }
  17.  
  18.  
  19. }
  20.  
Parsed in 0.077 seconds at 7.25 KB/s, using GeSHi 1.0.8.10

Get back to the Main.java and let's add a variable to hold the active Player object. Let's change the mFaceTextureRegion to mBallTextureRegion to make it pretty and change the type to TiledTextureRegion. My variables looks like this:


  1. // ===========================================================
  2. // Fields
  3. // ===========================================================
  4.  
  5. private ZoomCamera mCamera;
  6. private Texture mTexture;
  7. private TiledTextureRegion mBallTextureRegion;
  8. private TiledTextureRegion mPlayerTextureRegion;
  9. private SurfaceScrollDetector mScrollDetector;
  10. private TMXTiledMap mTMXTiledMap;
  11. private Player mActivePlayer;
Parsed in 0.106 seconds at 3.86 KB/s, using GeSHi 1.0.8.10

Here is the player sprite, download to your assets/gfx folder as usual.



Here is some experimental ball sprite (i need a real ball to make photos), to the same site, assets/gfx



Now we must change the way to load the textures to load this new two textures. All the textures go to the mTexture Object. The mTexture object need a power of two in width and height in pixels. To hold the textures recently downloaded we need exactly 256X256 pixels. The texture in memory would look like this:




Let's change en onLoadResources() method to load all this new stuff. First of all, the size of the mTexture needs to be 256X256 and now to load the ball texture we use the createTiledFromAsset() function with the parameters:

  • this.mTexture: The first is the mTexture object where all the textures are going
  • this: Actual class.
  • "gfx/ui_ball.png": Path to the image containing the tiled sprites.
  • 0,0: The next two integer parameters is where are going yo put the image inside mTexture. In this case, in the top left corner (0,0). The player in the top middle (128,0).
  • 2,4: Number of rows an columns in the tile. 2,4 in our case.
The code looks like this:


  1. @Override
  2. public void onLoadResources() {
  3. this.mTexture = new Texture(256, 256,
  4. TextureOptions.BILINEAR_PREMULTIPLYALPHA);
  5. this.mBallTextureRegion = TextureRegionFactory.createTiledFromAsset(
  6. this.mTexture, this, "gfx/ui_ball.png", 0, 0, 2, 4);
  7. this.mPlayerTextureRegion = TextureRegionFactory.createTiledFromAsset(
  8. this.mTexture, this, "gfx/ui_player.png", 128, 0, 2, 4);
  9.  
  10. this.mEngine.getTextureManager().loadTexture(this.mTexture);
  11.  
  12. }
Parsed in 0.074 seconds at 6.24 KB/s, using GeSHi 1.0.8.10

Now, some code on the onLoadScene() method. First we delete the source creating the ball and deactivate the ScrollDetector. The procedure should look like this:


  1. scene.setOnAreaTouchTraversalFrontToBack();
  2.  
  3. this.mScrollDetector = new SurfaceScrollDetector(this);
  4. this.mScrollDetector.setEnabled(false); //Hemos puesto esto a false
  5.  
  6. /*
  7.   final int centerX = (CAMERA_WIDTH - this.mFaceTextureRegion.getWidth()) / 2;
  8.   final int centerY = (CAMERA_HEIGHT - this.mFaceTextureRegion
  9.     .getHeight()) / 2;
  10.  
  11.   final Sprite ball = new Sprite(centerX, centerY,
  12.     this.mFaceTextureRegion);
  13.   scene.getLastChild().attachChild(ball);
  14.      */
  15. scene.setOnSceneTouchListener(this);
  16. scene.setTouchAreaBindingEnabled(true);
  17.  
  18. return scene;
  19. }
Parsed in 0.077 seconds at 7.57 KB/s, using GeSHi 1.0.8.10

Let's create a method to create a player to test the animation.


  1. // ===========================================================
  2. // Methods
  3. // ===========================================================
  4.  
  5. private void createPlayer() {
  6. final Scene scene = this.mEngine.getScene();
  7.  
  8. final Player sprite = new Player(200, 100, this.mPlayerTextureRegion);
  9. // scene.registerTouchArea(sprite);
  10.  
  11. scene.getLastChild().attachChild(sprite);
  12. this.mActivePlayer = sprite;
  13. }
Parsed in 0.069 seconds at 5.99 KB/s, using GeSHi 1.0.8.10

To test, we are gonna put a player in the 200X100 position. Call this new function from onLoadComplete().


  1. @Override
  2. public void onLoadComplete() { // scene.set 
  3. createPlayer();
  4. }
Parsed in 0.072 seconds at 1.08 KB/s, using GeSHi 1.0.8.10

In this point, we can debug our program to see what's happen. We can see the background grass and the player. It doesn't respond to scroll because we deactivated it. Let's create the detector.


Creating a Detector


Looking around in the AndEngine sources, the ScrollDetector is a very easy way to create Detectors.


We create a new class in our project. Name: CatapultDetector and SuperClass BaseDetector
(org.anddev.andengine.input.touch.detector.BaseDetector)


  1. package com.pruebas.andengine;
  2.  
  3. import org.anddev.andengine.input.touch.TouchEvent;
  4. import org.anddev.andengine.input.touch.detector.BaseDetector;
  5.  
  6. public class CatapultDetector extends BaseDetector {
  7.  
  8. @Override
  9. protected boolean onManagedTouchEvent(TouchEvent pSceneTouchEvent) {
  10. // TODO Auto-generated method stub
  11. return false;
  12. }
  13.  
  14. }
  15.  
Parsed in 0.091 seconds at 3.82 KB/s, using GeSHi 1.0.8.10

We are going to try to make a Detector that works like the ScrollDetector, we need a listener interface.


  1. public static interface ICatapultDetectorListener {
  2. // ===========================================================
  3. // Constants
  4. // ===========================================================
  5.  
  6. // ===========================================================
  7. // Methods
  8. // ===========================================================
  9.  
  10. public void onCharge(final CatapultDetector pCatapultDetector,
  11. final TouchEvent pTouchEvent, final float pDistance,
  12. final float pAngle);
  13.  
  14. public void onShoot(final CatapultDetector pCatapultDetector,
  15. final TouchEvent pTouchEvent, final float pDistance,
  16. final float pAngle);
  17. }
  18.  
Parsed in 0.102 seconds at 6.30 KB/s, using GeSHi 1.0.8.10


This is a basic listener to test the working of the Detector. The listener object recieves the angle and distance of the soot. We define this class inside the CatapultDetector class like the other detector in AndEngine.


Let's put some variables to the detector.


  1. // ===========================================================
  2. // Constants
  3. // ===========================================================
  4. private static final float TRIGGER_SCROLL_MINIMUM_DISTANCE_DEFAULT = 10;
  5. private static final float ANGLE_CONSTANT = 10;
  6. private static final int DEFAULT_STEPS = 6;
  7. private float DEFAULT_MAX_DISTANCE = 100;
  8. // ===========================================================
  9. // Fields
  10. // ===========================================================
  11.  
  12. //Minimum distance to execute 
  13. private float mTriggerScrollMinimumDistance;
  14.  
  15. //Listener for the Detector
  16. private final ICatapultDetectorListener mCatapultDetectorListener;
  17.  
  18. private boolean mTriggered;
  19.  
  20. //First Touch
  21. private float mFirstX;
  22. private float mFirstY;
  23.  
  24. //Last Touch
  25. private float mLastX;
  26. private float mLastY;
  27.  
  28. private int mSteps;
  29. private float mMaxDistance;
Parsed in 0.088 seconds at 10.05 KB/s, using GeSHi 1.0.8.10

We create some methods to use later and getter and setter to mTriggerScrollMinimumDistance


  1. // ===========================================================
  2. // Getter & Setter
  3. // ===========================================================
  4.  
  5. public void setTriggerScrollMinimumDistance(
  6. float mTriggerScrollMinimumDistance) {
  7. this.mTriggerScrollMinimumDistance = mTriggerScrollMinimumDistance;
  8. }
  9.  
  10. public float getTriggerScrollMinimumDistance() {
  11. return mTriggerScrollMinimumDistance;
  12. }
  13.  
  14. // ===========================================================
  15. // Methods
  16. // ===========================================================
  17.  
  18. protected float getX(final TouchEvent pTouchEvent) {
  19. return pTouchEvent.getX();
  20. }
  21.  
  22. protected float getY(final TouchEvent pTouchEvent) {
  23. return pTouchEvent.getY();
  24. }
Parsed in 0.098 seconds at 7.41 KB/s, using GeSHi 1.0.8.10

Code a little in the class constructors.


  1. // ===========================================================
  2. // Constructors
  3. // ===========================================================
  4.  
  5. public CatapultDetector(
  6. final ICatapultDetectorListener pCatapultDetectorListener) {
  7. this(TRIGGER_SCROLL_MINIMUM_DISTANCE_DEFAULT, pCatapultDetectorListener);
  8. }
  9.  
  10. public CatapultDetector(final float pTriggerScrollMinimumDistance,
  11. final ICatapultDetectorListener pCatapultDetectorListener) {
  12. this.setTriggerScrollMinimumDistance(pTriggerScrollMinimumDistance);
  13. this.mCatapultDetectorListener = pCatapultDetectorListener;
  14. }
Parsed in 0.077 seconds at 7.53 KB/s, using GeSHi 1.0.8.10

And now the main method of the class, where all happens.


  1. // ===========================================================
  2. // Methods for/from SuperClass/Interfaces
  3. // ===========================================================
  4.  
  5. @Override
  6. protected boolean onManagedTouchEvent(TouchEvent pSceneTouchEvent) {
  7. final float touchX = this.getX(pSceneTouchEvent);
  8. final float touchY = this.getY(pSceneTouchEvent);
  9.  
  10. switch (pSceneTouchEvent.getAction()) {
  11. case MotionEvent.ACTION_DOWN:
  12. this.mFirstX = touchX;
  13. this.mFirstY = touchY;
  14. this.mLastX = touchX;
  15. this.mLastY = touchY;
  16. this.mTriggered = false;
  17. return true;
  18. case MotionEvent.ACTION_MOVE:
  19. case MotionEvent.ACTION_UP:
  20. case MotionEvent.ACTION_CANCEL:
  21. final float distanceX = touchX - this.mLastX;
  22. final float distanceY = touchY - this.mLastY;
  23. if (pSceneTouchEvent.getAction() == MotionEvent.ACTION_MOVE) {
  24. final float triggerScrollMinimumDistance = this.mTriggerScrollMinimumDistance;
  25. if (this.mTriggered
  26. || Math.abs(distanceX) > triggerScrollMinimumDistance
  27. || Math.abs(distanceY) > triggerScrollMinimumDistance) {
  28. final float distance = (float)Math.hypot((double)distanceX,(double)distanceY);     
  29. final double angleX = touchX - this.mFirstX;
  30. final double angleY = touchY - this.mFirstY;
  31. final float angle = (float)Math.toDegrees(Math.atan2(angleY, angleX))+ANGLE_CONSTANT;
  32. this.mCatapultDetectorListener.onCharge(this, pSceneTouchEvent, distance, angle);
  33. this.mLastX = touchX;
  34. this.mLastY = touchY;
  35. this.mTriggered = true;
  36. }
  37. else
  38. {
  39.  
  40.  
  41. }
  42. }
  43. return true;
  44. default:
  45. return false;
  46. }
  47. }
  48.  
Parsed in 0.097 seconds at 16.70 KB/s, using GeSHi 1.0.8.10

In The next article we are going to explain more carefully the Detector, but looking the source you can see how it works. Let's back to the Main.java. Create a private variable to hold the Detector Object.


  1. // ===========================================================
  2. // Fields
  3. // ===========================================================
  4.  
  5. private ZoomCamera mCamera;
  6. private Texture mTexture;
  7. private TiledTextureRegion mBallTextureRegion;
  8. private TiledTextureRegion mPlayerTextureRegion;
  9. private SurfaceScrollDetector mScrollDetector; 
  10. private TMXTiledMap mTMXTiledMap;
  11. private Player mActivePlayer;
  12. private CatapultDetector mCatapultDetector; //Nueva variable Creada
Parsed in 0.078 seconds at 6.09 KB/s, using GeSHi 1.0.8.10

Now tell the Main class to implement the new ICatapultDetectorListener interface.


  1. public class Main extends BaseGameActivity implements IScrollDetectorListener,
  2. IOnSceneTouchListener, ICatapultDetectorListener
Parsed in 0.082 seconds at 1.54 KB/s, using GeSHi 1.0.8.10

Now we must implement the missing methods of the interface.


  1. @Override
  2. public void onCharge(CatapultDetector pCatapultDetector,
  3. TouchEvent pTouchEvent, float pDistance, float pAngle) {
  4. Log.d(TAG, "Cargando... {Distancia:" + pDistance + ", angulo: "
  5. + pAngle + "}");
  6. this.mActivePlayer.setRotation(pAngle);
  7.  
  8. }
  9.  
  10. @Override
  11. public void onShoot(CatapultDetector pCatapultDetector,
  12. TouchEvent pTouchEvent, float pDistance, float pAngle) {
  13. Log.d(TAG, "Disparo... {Distancia:" + pDistance + ", angulo: " + pAngle
  14. + "}");
  15. }
Parsed in 0.084 seconds at 5.75 KB/s, using GeSHi 1.0.8.10

In the onCharge() method, we rotate the player sprite according to the calculated angle. In the onShoot() method, only Debug message. The next step is create in the onLoadScene() method the Detector and assign to the Main Object.


  1. this.mScrollDetector = new SurfaceScrollDetector(this);
  2. this.mScrollDetector.setEnabled(false); 
  3.  
  4. //Creamos el detector de catapulta
  5. this.mCatapultDetector = new CatapultDetector(this);
  6. this.mCatapultDetector.setEnabled(true);
Parsed in 0.093 seconds at 2.55 KB/s, using GeSHi 1.0.8.10

And in the onSceneTouchEvent() method we pass the event to the CatapultDetector.



  1. @Override
  2. public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {
  3. if (this.mActivePlayer != null) {
  4. this.mCatapultDetector.onTouchEvent(pSceneTouchEvent);
  5. }
  6. return true;
  7. }
Parsed in 0.079 seconds at 2.59 KB/s, using GeSHi 1.0.8.10


We now can debug again the project and see what's going on. F11... we can see the splash, the background, the player... all fine. Let's tap with the finger on the Screen and do some move around. The payer rotates but not in the desired angle. It's a start. Now my files are like this:



Main.java
  1. package com.pruebas.andengine;
  2.  
  3. import org.anddev.andengine.engine.Engine;
  4. import org.anddev.andengine.engine.camera.ZoomCamera;
  5. import org.anddev.andengine.engine.options.EngineOptions;
  6. import org.anddev.andengine.engine.options.EngineOptions.ScreenOrientation;
  7. import org.anddev.andengine.engine.options.resolutionpolicy.RatioResolutionPolicy;
  8. import org.anddev.andengine.entity.layer.tiled.tmx.TMXLayer;
  9. import org.anddev.andengine.entity.layer.tiled.tmx.TMXLoader;
  10. import org.anddev.andengine.entity.layer.tiled.tmx.TMXProperties;
  11. import org.anddev.andengine.entity.layer.tiled.tmx.TMXTile;
  12. import org.anddev.andengine.entity.layer.tiled.tmx.TMXTileProperty;
  13. import org.anddev.andengine.entity.layer.tiled.tmx.TMXTiledMap;
  14. import org.anddev.andengine.entity.layer.tiled.tmx.TMXLoader.ITMXTilePropertiesListener;
  15. import org.anddev.andengine.entity.layer.tiled.tmx.util.exception.TMXLoadException;
  16. import org.anddev.andengine.entity.scene.Scene;
  17. import org.anddev.andengine.entity.scene.Scene.IOnSceneTouchListener;
  18. import org.anddev.andengine.entity.sprite.AnimatedSprite;
  19. import org.anddev.andengine.entity.sprite.Sprite;
  20. import org.anddev.andengine.entity.util.FPSLogger;
  21. import org.anddev.andengine.input.touch.TouchEvent;
  22. import org.anddev.andengine.input.touch.detector.ScrollDetector;
  23. import org.anddev.andengine.input.touch.detector.SurfaceScrollDetector;
  24. import org.anddev.andengine.input.touch.detector.ScrollDetector.IScrollDetectorListener;
  25. import org.anddev.andengine.opengl.texture.Texture;
  26. import org.anddev.andengine.opengl.texture.TextureOptions;
  27. import org.anddev.andengine.opengl.texture.region.TextureRegion;
  28. import org.anddev.andengine.opengl.texture.region.TextureRegionFactory;
  29. import org.anddev.andengine.opengl.texture.region.TiledTextureRegion;
  30. import org.anddev.andengine.ui.activity.BaseGameActivity;
  31. import org.anddev.andengine.util.Debug;
  32.  
  33. import android.util.Log;
  34.  
  35. import com.pruebas.andengine.CatapultDetector.ICatapultDetectorListener;
  36.  
  37. public class Main extends BaseGameActivity implements IScrollDetectorListener,
  38. IOnSceneTouchListener, ICatapultDetectorListener {
  39. // ===========================================================
  40. // Constants
  41. // ===========================================================
  42. static final int CAMERA_WIDTH = 480;
  43. static final int CAMERA_HEIGHT = 320;
  44.  
  45. private static final String TAG = "AndEngineTest";
  46.  
  47. // ===========================================================
  48. // Fields
  49. // ===========================================================
  50.  
  51. private ZoomCamera mCamera;
  52. private Texture mTexture;
  53. private TiledTextureRegion mBallTextureRegion;
  54. private TiledTextureRegion mPlayerTextureRegion;
  55. private SurfaceScrollDetector mScrollDetector; 
  56. private TMXTiledMap mTMXTiledMap;
  57. private Player mActivePlayer;
  58. private CatapultDetector mCatapultDetector; //Nueva variable Creada
  59.  
  60. // ===========================================================
  61. // Constructors
  62. // ===========================================================
  63.  
  64. // ===========================================================
  65. // Getter & Setter
  66. // ===========================================================
  67.  
  68. // ===========================================================
  69. // Methods for/from SuperClass/Interfaces
  70. // ===========================================================
  71.  
  72. @Override
  73. public void onLoadComplete() {  
  74. createPlayer();
  75. }
  76.  
  77. @Override
  78. public Engine onLoadEngine() {
  79. this.mCamera = new ZoomCamera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
  80. final int alturaTotal = CAMERA_HEIGHT * 3;
  81. this.mCamera.setBounds(0, CAMERA_WIDTH, 0, alturaTotal);
  82. this.mCamera.setBoundsEnabled(true);
  83. return new Engine(new EngineOptions(true, ScreenOrientation.LANDSCAPE,
  84. new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT),
  85. this.mCamera));
  86. }
  87.  
  88. @Override
  89. public void onLoadResources() {
  90. this.mTexture = new Texture(256, 256,
  91. TextureOptions.BILINEAR_PREMULTIPLYALPHA);
  92. this.mBallTextureRegion = TextureRegionFactory.createTiledFromAsset(
  93. this.mTexture, this, "gfx/ui_ball.png", 0, 0, 2, 4);
  94. this.mPlayerTextureRegion = TextureRegionFactory.createTiledFromAsset(
  95. this.mTexture, this, "gfx/ui_player.png", 128, 0, 2, 4);
  96.  
  97. this.mEngine.getTextureManager().loadTexture(this.mTexture);
  98.  
  99. }
  100.  
  101. @Override
  102. public Scene onLoadScene() {
  103. this.mEngine.registerUpdateHandler(new FPSLogger());
  104.  
  105. final Scene scene = new Scene(1);
  106.  
  107. try {
  108. final TMXLoader tmxLoader = new TMXLoader(this, this.mEngine
  109. .getTextureManager(), // TextureOptions.BILINEAR_PREMULTIPLYALPHA,
  110. TextureOptions.NEAREST, new ITMXTilePropertiesListener() {
  111.  
  112. @Override
  113. public void onTMXTileWithPropertiesCreated(
  114. final TMXTiledMap pTMXTiledMap,
  115. final TMXLayer pTMXLayer,
  116. final TMXTile pTMXTile,
  117. final TMXProperties<TMXTileProperty> pTMXTileProperties) {
  118.  
  119. }
  120.  
  121. });
  122. this.mTMXTiledMap = tmxLoader.loadFromAsset(this, "tmx/field.tmx");
  123. } catch (final TMXLoadException tmxle) {
  124. Debug.e(tmxle);
  125. }
  126.  
  127. final TMXLayer tmxLayer = this.mTMXTiledMap.getTMXLayers().get(0);
  128. scene.getFirstChild().attachChild(tmxLayer);
  129.  
  130. scene.setOnAreaTouchTraversalFrontToBack();
  131.  
  132. this.mScrollDetector = new SurfaceScrollDetector(this);
  133. this.mScrollDetector.setEnabled(false); 
  134.  
  135. this.mCatapultDetector = new CatapultDetector(this);
  136. this.mCatapultDetector.setEnabled(true);
  137.  
  138. scene.setOnSceneTouchListener(this);
  139. scene.setTouchAreaBindingEnabled(true);
  140.  
  141. return scene;
  142. }
  143.  
  144.  
  145. @Override
  146. public void onCharge(CatapultDetector pCatapultDetector,
  147. TouchEvent pTouchEvent, float pDistance, float pAngle) {
  148. Log.d(TAG, "Cargando... {Distancia:" + pDistance + ", angulo: "
  149. + pAngle + "}");
  150. this.mActivePlayer.setRotation(pAngle);
  151.  
  152. }
  153.  
  154. @Override
  155. public void onShoot(CatapultDetector pCatapultDetector,
  156. TouchEvent pTouchEvent, float pDistance, float pAngle) {
  157. Log.d(TAG, "Disparo... {Distancia:" + pDistance + ", angulo: " + pAngle
  158. + "}");
  159. } 
  160.  
  161.  
  162. @Override
  163. public void onScroll(ScrollDetector pScollDetector, TouchEvent pTouchEvent,
  164. float pDistanceX, float pDistanceY) {
  165. this.mCamera.offsetCenter(-pDistanceX, -pDistanceY);
  166. }
  167.  
  168. @Override
  169. public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {
  170. if (this.mActivePlayer != null) {
  171. this.mCatapultDetector.onTouchEvent(pSceneTouchEvent);
  172. }
  173. return true;
  174. }
  175.  
  176. // ===========================================================
  177. // Methods
  178. // ===========================================================
  179.  
  180. private void createPlayer() {
  181. final Scene scene = this.mEngine.getScene();
  182.  
  183. final Player sprite = new Player(200, 100, this.mPlayerTextureRegion);
  184. scene.getLastChild().attachChild(sprite);
  185.  
  186. this.mActivePlayer = sprite;
  187. } 
  188.  
  189.  
  190. // ===========================================================
  191. // Inner and Anonymous Classes
  192. // ===========================================================
  193. }
  194.  
Parsed in 0.129 seconds at 53.47 KB/s, using GeSHi 1.0.8.10


CatapultDetector.java
  1. package com.pruebas.andengine;
  2.  
  3. import org.anddev.andengine.input.touch.TouchEvent;
  4. import org.anddev.andengine.input.touch.detector.BaseDetector;
  5.  
  6. import android.view.MotionEvent;
  7.  
  8. public class CatapultDetector extends BaseDetector {
  9. // ===========================================================
  10. // Constants
  11. // ===========================================================
  12. private static final float TRIGGER_SCROLL_MINIMUM_DISTANCE_DEFAULT = 10;
  13. private static final float ANGLE_CONSTANT = 10;
  14. private static final int DEFAULT_STEPS = 6;
  15. private float DEFAULT_MAX_DISTANCE = 100;
  16. // ===========================================================
  17. // Fields
  18. // ===========================================================
  19.  
  20. //Minimum distance to execute 
  21. private float mTriggerScrollMinimumDistance;
  22.  
  23. //Listener for the Detector
  24. private final ICatapultDetectorListener mCatapultDetectorListener;
  25.  
  26. private boolean mTriggered;
  27.  
  28. //First Touch
  29. private float mFirstX;
  30. private float mFirstY;
  31.  
  32. //Last Touch
  33. private float mLastX;
  34. private float mLastY;
  35.  
  36. private int mSteps;
  37. private float mMaxDistance;
  38.  
  39. // ===========================================================
  40. // Constructors
  41. // ===========================================================
  42.  
  43. public CatapultDetector(
  44. final ICatapultDetectorListener pCatapultDetectorListener) {
  45. this(TRIGGER_SCROLL_MINIMUM_DISTANCE_DEFAULT, pCatapultDetectorListener);
  46. }
  47.  
  48. public CatapultDetector(final float pTriggerScrollMinimumDistance,
  49. final ICatapultDetectorListener pCatapultDetectorListener) {
  50. this.setTriggerScrollMinimumDistance(pTriggerScrollMinimumDistance);
  51. this.mCatapultDetectorListener = pCatapultDetectorListener;
  52. }
  53.  
  54.  
  55.  
  56. // ===========================================================
  57. // Methods for/from SuperClass/Interfaces
  58. // =========================================================== 
  59.  
  60. @Override
  61. protected boolean onManagedTouchEvent(TouchEvent pSceneTouchEvent) {
  62. final float touchX = this.getX(pSceneTouchEvent);
  63. final float touchY = this.getY(pSceneTouchEvent);
  64.  
  65. switch (pSceneTouchEvent.getAction()) {
  66. case MotionEvent.ACTION_DOWN:
  67. this.mFirstX = touchX;
  68. this.mFirstY = touchY;
  69. this.mLastX = touchX;
  70. this.mLastY = touchY;
  71. this.mTriggered = false;
  72. return true;
  73. case MotionEvent.ACTION_MOVE:
  74. case MotionEvent.ACTION_UP:
  75. case MotionEvent.ACTION_CANCEL:
  76. final float distanceX = touchX - this.mLastX;
  77. final float distanceY = touchY - this.mLastY;
  78. if (pSceneTouchEvent.getAction() == MotionEvent.ACTION_MOVE) {
  79. final float triggerScrollMinimumDistance = this.mTriggerScrollMinimumDistance;
  80. if (this.mTriggered
  81. || Math.abs(distanceX) > triggerScrollMinimumDistance
  82. || Math.abs(distanceY) > triggerScrollMinimumDistance) {
  83. final float distance = (float)Math.hypot((double)distanceX,(double)distanceY);     
  84. final double angleX = touchX - this.mFirstX;
  85. final double angleY = touchY - this.mFirstY;
  86. final float angle = (float)Math.toDegrees(Math.atan2(angleY, angleX));
  87. this.mCatapultDetectorListener.onCharge(this, pSceneTouchEvent, distance, angle);
  88. this.mLastX = touchX;
  89. this.mLastY = touchY;
  90. this.mTriggered = true;
  91. }
  92. else
  93. {
  94.  
  95.  
  96. }
  97. }
  98. return true;
  99. default:
  100. return false;
  101. }
  102. }
  103.  
  104. // ===========================================================
  105. // Getter & Setter
  106. // ===========================================================
  107.  
  108. public void setTriggerScrollMinimumDistance(
  109. float mTriggerScrollMinimumDistance) {
  110. this.mTriggerScrollMinimumDistance = mTriggerScrollMinimumDistance;
  111. }
  112.  
  113. public float getTriggerScrollMinimumDistance() {
  114. return mTriggerScrollMinimumDistance;
  115. }
  116.  
  117. // ===========================================================
  118. // Methods
  119. // ===========================================================
  120.  
  121. protected float getX(final TouchEvent pTouchEvent) {
  122. return pTouchEvent.getX();
  123. }
  124.  
  125. protected float getY(final TouchEvent pTouchEvent) {
  126. return pTouchEvent.getY();
  127. } 
  128.  
  129.  
  130. // ===========================================================
  131. // Inner and Anonymous Classes
  132. // =========================================================== 
  133.  
  134. public static interface ICatapultDetectorListener {
  135. // ===========================================================
  136. // Constants
  137. // ===========================================================
  138.  
  139. // ===========================================================
  140. // Methods
  141. // ===========================================================
  142.  
  143. public void onCharge(final CatapultDetector pCatapultDetector,
  144. final TouchEvent pTouchEvent, final float pDistance,
  145. final float pAngle);
  146.  
  147. public void onShoot(final CatapultDetector pCatapultDetector,
  148. final TouchEvent pTouchEvent, final float pDistance,
  149. final float pAngle);
  150. }
  151.  
  152. }
  153.  
Parsed in 0.126 seconds at 38.60 KB/s, using GeSHi 1.0.8.10

Good work today. I'm working on this Detector. The next chapter in the same subject.



Source Code

To get the source code: svn checkout http://ch-soccer.googlecode.com/svn/trunk/ tutorial-read-only -r 7


Index

15 comments:

  1. Basically it force closes after the splash screen because for some reason it first can't find 'libhgl.so'

    Then it tells me it 'Failed loading bitmap into AssestTextureSource'

    Then of course a lot of red after that...

    ReplyDelete
  2. Did you tried the Source from the subversion??

    svn checkout http://ch-soccer.googlecode.com/svn/trunk/ tutorial-read-only -r 7

    If that doesn't work, try Project -> Clean in Eclipse. The sources are tested to work.

    ReplyDelete
  3. Yes I copied the source for every thing from that location but same thing happens. Apparently someone else was having that problem. Is the libhgl.so library the problem or is it something to do with how my tmx file is setup. What all goes into the TMX folder exactly. Does it just contain the .tmx you save from TiledQT or do you have to do something else with it. The tutorial was vague in that area in tutorial 3. Sorry to bother about this but I really really want it work and I would like to put this engine to use.

    I'm also very new to Android programming so I appreciate you taking the time to bear through this with me. I saw where someone else was having the same problem here too.

    Another thing is there are so many versions of the andengine.jar file that I'm not sure which one to use. For your example I used the one at
    http://ch-soccer.googlecode.com/svn/trunk/ tutorial-read-only -r 7

    But if you try to use that jar with other tutorials, some of the methods have changed.

    Tutorial 1 and 2 went perfect, but as soon as we introduce the Splashcreen and TMX stuff, that's where I have the problem and I think it's because I'm doing something wrong with the .tmx or there is something missing in regards to the level file.

    I noticed here ( http://ch-soccer.googlecode.com/svn/trunk/ tutorial-read-only -r 7 ) that there is a file in the tmx folder calle field.tmx but when I look at it it's in xml code. What is going on there exactly?

    Thank You

    ReplyDelete
  4. Please try the next thing. Go to Debug perspective, and debug your project.

    There is down-right a LogCat pagecontrol, go to the Log Page, and push the E button in the upper toolbar of the pagecontrol.

    Then Clear log (another button on the toolbar) and push F11 to debug the project.

    Plase send me the result of the debug, there is the exact message of the error. If you don't tell me exactly what error is happening, i can't help you, all i can tell you is with an empty workspace i imported the project from the cvs and all works fine in the emulator and in a terminal.

    Please, send me the error to angel.ivorra at gmail dot com and i will try to solve your problem.

    Thank you

    ReplyDelete
  5. I did get these errors after I thought I had it all perfect... still force closes no matter what I do. I've checked every line of code and I still think it has something to do with the TMX file. That's the only place I'm a little vague, or not fully comprehending. These are the only errors I get but I don't think it will help.

    [2011-04-11 16:28:22 - Logcat]null
    com.android.ddmlib.TimeoutException
    at com.android.ddmlib.AdbHelper.read(AdbHelper.java:648)
    at com.android.ddmlib.AdbHelper.read(AdbHelper.java:616)
    at com.android.ddmlib.AdbHelper.readAdbResponse(AdbHelper.java:216)
    at com.android.ddmlib.AdbHelper.executeRemoteCommand(AdbHelper.java:378)
    at com.android.ddmlib.Device.executeShellCommand(Device.java:284)
    at com.android.ddmuilib.logcat.LogPanel$3.run(LogPanel.java:527)

    ReplyDelete
  6. It can't load/find the .tmx file:


    04-12 14:33:08.198: ERROR/AndEngine(331): AndEngine
    04-12 14:33:08.198: ERROR/AndEngine(331): org.anddev.andengine.entity.layer.tiled.tmx.util.exception.TMXLoadException: Could not load TMXTiledMap from asset: tmx/field.tmx
    04-12 14:33:08.198: ERROR/AndEngine(331): at org.anddev.andengine.entity.layer.tiled.tmx.TMXLoader.loadFromAsset(TMXLoader.java:77)
    04-12 14:33:08.198: ERROR/AndEngine(331): at com.pruebas.andengine.Main.onLoadScene(Main.java:108)
    04-12 14:33:08.198: ERROR/AndEngine(331): at org.anddev.andengine.ui.activity.BaseGameActivity.doResume(BaseGameActivity.java:159)
    04-12 14:33:08.198: ERROR/AndEngine(331): at org.anddev.andengine.ui.activity.BaseGameActivity.onWindowFocusChanged(BaseGameActivity.java:83)
    04-12 14:33:08.198: ERROR/AndEngine(331): at com.android.internal.policy.impl.PhoneWindow$DecorView.onWindowFocusChanged(PhoneWindow.java:1981)
    04-12 14:33:08.198: ERROR/AndEngine(331): at android.view.View.dispatchWindowFocusChanged(View.java:3788)
    04-12 14:33:08.198: ERROR/AndEngine(331): at android.view.ViewGroup.dispatchWindowFocusChanged(ViewGroup.java:658)
    04-12 14:33:08.198: ERROR/AndEngine(331): at android.view.ViewRoot.handleMessage(ViewRoot.java:1921)
    04-12 14:33:08.198: ERROR/AndEngine(331): at android.os.Handler.dispatchMessage(Handler.java:99)
    04-12 14:33:08.198: ERROR/AndEngine(331): at android.os.Looper.loop(Looper.java:123)
    04-12 14:33:08.198: ERROR/AndEngine(331): at android.app.ActivityThread.main(ActivityThread.java:4627)
    04-12 14:33:08.198: ERROR/AndEngine(331): at java.lang.reflect.Method.invokeNative(Native Method)
    04-12 14:33:08.198: ERROR/AndEngine(331): at java.lang.reflect.Method.invoke(Method.java:521)
    04-12 14:33:08.198: ERROR/AndEngine(331): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
    04-12 14:33:08.198: ERROR/AndEngine(331): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
    04-12 14:33:08.198: ERROR/AndEngine(331): at dalvik.system.NativeStart.main(Native Method)
    04-12 14:33:08.198: ERROR/AndEngine(331): Caused by: java.io.FileNotFoundException: tmx/field.tmx
    04-12 14:33:08.198: ERROR/AndEngine(331): at android.content.res.AssetManager.openAsset(Native Method)
    04-12 14:33:08.198: ERROR/AndEngine(331): at android.content.res.AssetManager.open(AssetManager.java:313)
    04-12 14:33:08.198: ERROR/AndEngine(331): at android.content.res.AssetManager.open(AssetManager.java:287)

    ReplyDelete
  7. There is more but couldn't fit in one post. Let me know if that helps. Does the tmx folder go into the root of the project instead or something...

    ReplyDelete
  8. Make a Project Clean, this worked for me several times.

    If it doesn't help, make sure that the preferences of qtiled are like i said in the third tutorial.

    Hope it helps you.

    ReplyDelete
  9. OMG I'm just gonna stop posting.... I didn't have the tmx file in there this time but I did before and still get the force close looking for errors now that I know how to debug better.

    ReplyDelete
  10. I did do the Base64 Gzip. Are you tiling 4 horizontal and 8 vertical tiles for the field and are you naming the .tmx field.tmx for certain?

    thx

    ReplyDelete
  11. Yes, you can download the source code from the subversion server.

    ReplyDelete
  12. Another great part of the Tutorial, I'm confused about Detector.
    Need study it more

    ReplyDelete
  13. Hi Angel,
    The app is working whitout any problem, but my background(tmx) doesn't look like how I create on virtual machine. It is a different one. I mean tiles look displaced.
    How can it be possible?

    P.S: thx very much for the tutorial

    Bartu

    ReplyDelete
  14. After a lot of fighting with a crash right after the splash screen I found this issue: http://code.google.com/p/andengine/issues/detail?id=53

    Rename the images to something without the underscore to fix the crashes where it cannot find the .png files.

    ReplyDelete
  15. The app is working great!

    For those with a problem, check the name of your field file, and the position. You probably named it to something else.

    ReplyDelete