Monday, March 21, 2011

AndEngine from Scratch (II)

Previously on AndEngine from Scratch



Creating our first object


Before we create our first object, we are gonna touch the AndroidManifest.xml to add the permission to lock the phone from suspension. We go to the AndroidManifest.xml file in the root of our project and edit the file as xml. After the line

  1. <uses-sdk android:minSdkVersion="4" />
Parsed in 0.001 seconds at 29.50 KB/s, using GeSHi 1.0.8.10

We must put

  1. <uses-permission android:name="android.permission.WAKE_LOCK"/>
Parsed in 0.001 seconds at 80.72 KB/s, using GeSHi 1.0.8.10


Let's create our first object in screen. To use images and other resources, Andengine doesn't use the typcal R from Android development. We must create a folder in assets called gfx.





Now download this image to your hard drive and put it in the assets/gfx recently created folder, you can directly save to the desired location or drag from your folder to the eclipse gfx folder.



The next step is modify our source in Eclipse to show the image. In the part when we defined the camera, we must add two new variables.

  1. // ===========================================================
  2. // Fields
  3. // ===========================================================
  4.  
  5. private ZoomCamera mCamera;
  6. private Texture mTexture;
  7. private TextureRegion mFaceTextureRegion;
Parsed in 0.000 seconds at 1133.54 KB/s, using GeSHi 1.0.8.10

In the onLoadResources() method need to load the image to memory.


  1. @Override
  2. public void onLoadResources() {
  3. this.mTexture = new Texture(64, 64, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
  4. this.mFaceTextureRegion = TextureRegionFactory.createFromAsset(this.mTexture, this, "gfx/ui_ball_1.png", 0, 0);
  5.  
  6. this.mEngine.getTextureManager().loadTexture(this.mTexture);
  7.  
  8. }
Parsed in 0.082 seconds at 3.62 KB/s, using GeSHi 1.0.8.10

And finally, in the onLoadScene() method put some code to show the image centered in the screen


  1. @Override
  2. public Scene onLoadScene() {
  3. this.mEngine.registerUpdateHandler(new FPSLogger());
  4.  
  5. final Scene scene = new Scene(1);
  6. scene.setBackground(new ColorBackground(0, 0, 0.8784f));
  7.  
  8. final int centerX = (CAMERA_WIDTH - this.mFaceTextureRegion.getWidth()) / 2;
  9. final int centerY = (CAMERA_HEIGHT - this.mFaceTextureRegion.getHeight()) / 2;
  10.  
  11. /* Dibujamos la bola en el centro de la pantalla. */
  12. final Sprite ball = new Sprite(centerX, centerY, this.mFaceTextureRegion);
  13. scene.getLastChild().attachChild(ball);
  14.  
  15.  
  16. return scene;
  17. }
Parsed in 0.091 seconds at 5.85 KB/s, using GeSHi 1.0.8.10

Now run our project and we can see what we expected on the screen.



Until now everything runs correctly. Now let's control the camera movement touching the screen.



Constrolling Scroll


In order to the scroll to work, we need our Main class to implement two interfaces.


  • IScrollDetectorListener: This class that AndEngine bring to us is to control the scroll movement. We must add the interface and implement the necessary methods.

  • IOnSceneTouchListener: This class give us the posibility to control the touchScreen.

The class looks like this:

  1. public class Main extends BaseGameActivity implements IScrollDetectorListener, IOnSceneTouchListener 
Parsed in 0.072 seconds at 1.37 KB/s, using GeSHi 1.0.8.10

Let's implement the two necessary methods-

  1. public void onScroll(ScrollDetector pScollDetector, TouchEvent pTouchEvent,
  2. float pDistanceX, float pDistanceY) {
  3. }
  4.  
  5. @Override
  6. public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {
  7. return true;
  8. }
Parsed in 0.079 seconds at 2.81 KB/s, using GeSHi 1.0.8.10

Now let's put some code into the methods. In the onScroll we put only a Debug Message.

  1. public void onScroll(ScrollDetector pScollDetector, TouchEvent pTouchEvent,
  2. float pDistanceX, float pDistanceY) {
  3. Log.d(TAG, "Scroll {x:"+pDistanceX+", y: "+pDistanceY+"}");  
  4. }
Parsed in 0.076 seconds at 2.31 KB/s, using GeSHi 1.0.8.10

In the TouchEvent, we pass directly the params to the ScrollDetector.

  1. public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {
  2. this.mScrollDetector.onTouchEvent(pSceneTouchEvent);
  3. return true;
  4. }
Parsed in 0.077 seconds at 1.88 KB/s, using GeSHi 1.0.8.10

We must touch a little the LoadScene() method in order to the Engine Listen the TouchScreen events.

  1. public Scene onLoadScene() {
  2.  
  3. this.mEngine.registerUpdateHandler(new FPSLogger());
  4.  
  5. final Scene scene = new Scene(1);
  6. scene.setBackground(new ColorBackground(0, 0, 0.8784f));
  7. scene.setOnAreaTouchTraversalFrontToBack();
  8.  
  9. this.mScrollDetector = new SurfaceScrollDetector(this);
  10. this.mScrollDetector.setEnabled(true);
  11.  
  12.  
  13. final int centerX = (CAMERA_WIDTH - this.mFaceTextureRegion.getWidth()) / 2;
  14. final int centerY = (CAMERA_HEIGHT - this.mFaceTextureRegion.getHeight()) / 2;
  15.  
  16. /* Dibujamos la bola en el centro de la pantalla. */
  17. final Sprite ball = new Sprite(centerX, centerY, this.mFaceTextureRegion);
  18. scene.getLastChild().attachChild(ball);
  19.  
  20. scene.setOnSceneTouchListener(this);
  21. scene.setTouchAreaBindingEnabled(true);
  22.  
  23. return scene;
  24. }
Parsed in 0.084 seconds at 8.84 KB/s, using GeSHi 1.0.8.10

Add the ScrollDetector to Fields.

  1. // ===========================================================
  2. // Fields
  3. // ===========================================================
  4.  
  5. private ZoomCamera mCamera;
  6. private Texture mTexture;
  7. private TextureRegion mFaceTextureRegion;
  8. private SurfaceScrollDetector mScrollDetector;
Parsed in 0.080 seconds at 3.48 KB/s, using GeSHi 1.0.8.10

Let's go to the Debug Perspective and let's define to filters to see the information in a confortable way. We must create "AndEngineTest" and "AndEngine" filters.

Now we have the Eclipse ready to see some information about what's going on. Debug the project (F11), switch to Debug Perspective and tap with the finger in the screen (or the mouse in the emulator) and move around. This is what we must see in the debug window.

Now, the Main.java file looks 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.scene.Scene;
  9. import org.anddev.andengine.entity.scene.Scene.IOnSceneTouchListener;
  10. import org.anddev.andengine.entity.scene.background.ColorBackground;
  11. import org.anddev.andengine.entity.sprite.Sprite;
  12. import org.anddev.andengine.entity.util.FPSLogger;
  13. import org.anddev.andengine.input.touch.TouchEvent;
  14. import org.anddev.andengine.input.touch.detector.ScrollDetector;
  15. import org.anddev.andengine.input.touch.detector.SurfaceScrollDetector;
  16. import org.anddev.andengine.input.touch.detector.ScrollDetector.IScrollDetectorListener;
  17. import org.anddev.andengine.opengl.texture.Texture;
  18. import org.anddev.andengine.opengl.texture.TextureOptions;
  19. import org.anddev.andengine.opengl.texture.region.TextureRegion;
  20. import org.anddev.andengine.opengl.texture.region.TextureRegionFactory;
  21. import org.anddev.andengine.ui.activity.BaseGameActivity;
  22.  
  23. import android.util.Log;
  24.  
  25. public class Main extends BaseGameActivity implements IScrollDetectorListener,
  26. IOnSceneTouchListener {
  27. // ===========================================================
  28. // Constants
  29. // ===========================================================
  30. static final int CAMERA_WIDTH = 480;
  31. static final int CAMERA_HEIGHT = 320;
  32.  
  33. private static final String TAG = "AndEngineTest";
  34.  
  35. // ===========================================================
  36. // Fields
  37. // ===========================================================
  38.  
  39. private ZoomCamera mCamera;
  40. private Texture mTexture;
  41. private TextureRegion mFaceTextureRegion;
  42. private SurfaceScrollDetector mScrollDetector;
  43.  
  44. // ===========================================================
  45. // Constructors
  46. // ===========================================================
  47.  
  48. // ===========================================================
  49. // Getter & Setter
  50. // ===========================================================
  51.  
  52. // ===========================================================
  53. // Methods for/from SuperClass/Interfaces
  54. // ===========================================================
  55.  
  56. @Override
  57. public void onLoadComplete() {
  58. // TODO Auto-generated method stub
  59.  
  60. }
  61.  
  62. @Override
  63. public Engine onLoadEngine() {
  64. this.mCamera = new ZoomCamera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
  65. return new Engine(new EngineOptions(true, ScreenOrientation.LANDSCAPE,
  66. new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT),
  67. this.mCamera));
  68. }
  69.  
  70. @Override
  71. public void onLoadResources() {
  72. this.mTexture = new Texture(64, 64,
  73. TextureOptions.BILINEAR_PREMULTIPLYALPHA);
  74. this.mFaceTextureRegion = TextureRegionFactory.createFromAsset(
  75. this.mTexture, this, "gfx/ui_ball_1.png", 0, 0);
  76.  
  77. this.mEngine.getTextureManager().loadTexture(this.mTexture);
  78.  
  79. }
  80.  
  81. @Override
  82. public Scene onLoadScene() {
  83. this.mEngine.registerUpdateHandler(new FPSLogger());
  84.  
  85. final Scene scene = new Scene(1);
  86. scene.setBackground(new ColorBackground(0, 0, 0.8784f));
  87. scene.setOnAreaTouchTraversalFrontToBack();
  88.  
  89. this.mScrollDetector = new SurfaceScrollDetector(this);
  90. this.mScrollDetector.setEnabled(true);
  91.  
  92. final int centerX = (CAMERA_WIDTH - this.mFaceTextureRegion.getWidth()) / 2;
  93. final int centerY = (CAMERA_HEIGHT - this.mFaceTextureRegion
  94. .getHeight()) / 2;
  95.  
  96. /* Dibujamos la bola en el centro de la pantalla. */
  97. final Sprite ball = new Sprite(centerX, centerY,
  98. this.mFaceTextureRegion);
  99. scene.getLastChild().attachChild(ball);
  100.  
  101. scene.setOnSceneTouchListener(this);
  102. scene.setTouchAreaBindingEnabled(true);
  103.  
  104. return scene;
  105. }
  106.  
  107. @Override
  108. public void onScroll(ScrollDetector pScollDetector, TouchEvent pTouchEvent,
  109. float pDistanceX, float pDistanceY) {
  110. Log.d(TAG, "Scroll {x:" + pDistanceX + ", y: " + pDistanceY + "}");
  111. }
  112.  
  113. @Override
  114. public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {
  115. this.mScrollDetector.onTouchEvent(pSceneTouchEvent);
  116. return true;
  117. }
  118.  
  119. // ===========================================================
  120. // Methods
  121. // ===========================================================
  122.  
  123. // ===========================================================
  124. // Inner and Anonymous Classes
  125. // ===========================================================
  126. }
  127.  
Parsed in 0.117 seconds at 37.52 KB/s, using GeSHi 1.0.8.10

In the AndEngine filter, we can see some AndEngine messages, inicialization, and the FPS of the game. In the AndEngineTest fiter, we can see the debug messages we put in the sources.

In my case, i have moved the finger first up and then down verticaly, and in the debug i can see correct messages. Changing values in the Y axis.

We have suscefully detected the movement of the finger on the screen, and AndEngine bring to us some classes to make the process easier.Let's define some boundaries.

Controling the camera

Some code to put on the onScroll() method

  1. @Override
  2. public void onScroll(ScrollDetector pScollDetector, TouchEvent pTouchEvent,
  3. float pDistanceX, float pDistanceY) {
  4. this.mCamera.offsetCenter(-pDistanceX, -pDistanceY);
  5. } 
Parsed in 0.079 seconds at 2.26 KB/s, using GeSHi 1.0.8.10

We debug and we now can move the camera with scroll movements of the finger. But it has no limits, we need to define them.

  1. @Override
  2. public Engine onLoadEngine() {
  3. this.mCamera = new ZoomCamera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);  
  4. final int alturaTotal = CAMERA_HEIGHT*3;
  5. this.mCamera.setBounds(0, CAMERA_WIDTH, 0, alturaTotal);
  6. this.mCamera.setBoundsEnabled(true);
  7. return new Engine(new EngineOptions(true, ScreenOrientation.LANDSCAPE,
  8. new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT),
  9. this.mCamera));
  10. }
Parsed in 0.088 seconds at 4.41 KB/s, using GeSHi 1.0.8.10

Well... that ends the phase two. The next lesson we are going to put some background, and play with sprites.

Source Code

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

Index

20 comments:

  1. I followed this example and it almost works. I can drag the ball right (from the middle.) But, it never goes up or down or left from the middle...How come is that?

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. Actually you can adjust the parameters of mCamera.setBounds().

    ReplyDelete
  4. pitosalas:
    If you want to move the ball instead of the camera, make the ball sprite public by moving the definition from onLoadScene to the top, and change this line:
    this.mCamera.offsetCenter(-pDistanceX, -pDistanceY);

    to this:
    ball.setPosition( ball.getX() + pDistanceX, ball.getY() + pDistanceY);

    ReplyDelete
  5. Thanks for your tutorial! There is not much documentation on AndEngine, so every bit is highly welcomed... :)

    Just a note: In the latest version of AndEngine, the texture loading does not work anymore as described here (only if the jar is built from scratch). Took me a bit of surfing through the code. Help can be found here: http://code.google.com/p/andengineexamples/source/browse/src/org/anddev/andengine/examples/LoadTextureExample.java

    ReplyDelete
  6. Thank you for the comment. I have to change some code to reflect the changes in andengine and upload the updated source code of the project, and some other tutorials. I 've a game upcoming in the market in the next weeks and i will continue with this job.

    Thank all for the comments... you encourage me to continue with the work. Any help would be appreciated.

    ReplyDelete
  7. I had to change it to:

    [code]
    @Override
    public void onLoadResources() {
    this.mTexture = new BitmapTextureAtlas(64, 64, TextureOptions.BILINEAR_PREMULTIPLYALPHA);

    this.mFaceTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset
    (this.mTexture, this, "gfx/ui_ball_1.png", 0, 0);

    this.mEngine.getTextureManager().loadTexture(this.mTexture);
    }

    @Override
    public Scene onLoadScene() {
    this.mEngine.registerUpdateHandler(new FPSLogger());

    final Scene scene = new Scene();//(1);

    scene.setBackground(new ColorBackground(0, 0, 0.8784f));

    final int centerX = (CAMERA_WIDTH - this.mFaceTextureRegion.getWidth()) / 2;
    final int centerY = (CAMERA_HEIGHT - this.mFaceTextureRegion.getHeight()) / 2;

    /* Dibujamos la bola en el centro de la pantalla. */

    final Sprite ball = new Sprite(centerX, centerY, this.mFaceTextureRegion);

    scene.attachChild(ball);
    //scene.getLastChild().attachChild(ball);

    return scene;
    }
    [/code]

    ReplyDelete
  8. Hi,

    I have write your as it given above but when I touch on Screen the error is occurred.

    "The Application stopped Unexpectedly. Please Try again." Button is appeared Force Close.

    Why its happened?????

    ReplyDelete
  9. @Adnan, I think you may have forgotten to load the ball picture into the Texture object. I was following the tutorial line by line and can't figure out how I missed it. If your onLoadResources method is empty, then that's where the problem is.

    ReplyDelete
  10. @Adnan, You'll also get the same error if you don't change the last line in onLoadScene().

    From Lucretia9's edit:
    scene.attachChild(ball);
    //scene.getLastChild().attachChild(ball);

    ReplyDelete
  11. greatest tutorial I've ever seen. thanks a lot for this very very helpful tutorial

    ReplyDelete
  12. Thanks for this post!
    But do we have a way to perform as a scrollview. I means
    the motion after we stop scrolling, the screen would scroll a bit more after we leave touching?

    ReplyDelete
  13. Of course, don't forget to change your declaration of mTexture:


    private BitmapTextureAtlas mTexture;

    ReplyDelete
  14. Thanks for the tutorial ! Very useful !

    ReplyDelete
  15. I have problem in method onLoadResources, this my source:
    1. this.mTexture = new Texture(66, 64, TextureOptions.BILINEAR_PREMULTIPLYALPHA);

    there is an error message, that is : cannot instantiae the type texture.

    2. this.mFaceTextureRegion = TextureRegionFactory.createFromAsset(this.mTexture, this, "gfx/ui_ball_1.png", 0, 0);

    there is an error message, that is : The method createFromAsset(Texture, SpriteExampleActivity, String, int, int) is undefined for the type
    TextureRegionFactory

    whats the problem?.. help me please.. thanks..

    ReplyDelete
  16. I have the same problem as erctz. Eclips expects BuildableTexture instead of Texture. Help us please!

    ReplyDelete
  17. I've changed type of mTexture on BuildableTexture everywhere. Now there is no error but no ball too...only empty scene

    ReplyDelete
  18. When I Run the application,am getting this error

    04-18 20:54:45.259: WARN/dalvikvm(491): threadid=1: thread exiting with uncaught exception (group=0x4001d800)
    04-18 20:54:45.319: ERROR/AndroidRuntime(491): FATAL EXCEPTION: main
    04-18 20:54:45.319: ERROR/AndroidRuntime(491): java.lang.IllegalArgumentException: Supplied pTextureSource must not exceed bounds of Texture.
    04-18 20:54:45.319: ERROR/AndroidRuntime(491): at org.anddev.andengine.opengl.texture.Texture.checkTextureSourcePosition(Texture.java:145)
    04-18 20:54:45.319: ERROR/AndroidRuntime(491): at org.anddev.andengine.opengl.texture.Texture.addTextureSource(Texture.java:131)
    04-18 20:54:45.319: ERROR/AndroidRuntime(491): at org.anddev.andengine.opengl.texture.region.TextureRegionFactory.createFromSource(TextureRegionFactory.java:89)
    04-18 20:54:45.319: ERROR/AndroidRuntime(491): at org.anddev.andengine.opengl.texture.region.TextureRegionFactory.createFromAsset(TextureRegionFactory.java:67)
    04-18 20:54:45.319: ERROR/AndroidRuntime(491): at andengine.test.AndEngineTestActivity.onLoadResources(AndEngineTestActivity.java:74)
    04-18 20:54:45.319: ERROR/AndroidRuntime(491): at org.anddev.andengine.ui.activity.BaseGameActivity.doResume(BaseGameActivity.java:158)
    04-18 20:54:45.319: ERROR/AndroidRuntime(491): at org.anddev.andengine.ui.activity.BaseGameActivity.onWindowFocusChanged(BaseGameActivity.java:83)
    04-18 20:54:45.319: ERROR/AndroidRuntime(491): at com.android.internal.policy.impl.PhoneWindow$DecorView.onWindowFocusChanged(PhoneWindow.java:1981)
    04-18 20:54:45.319: ERROR/AndroidRuntime(491): at android.view.View.dispatchWindowFocusChanged(View.java:3788)
    04-18 20:54:45.319: ERROR/AndroidRuntime(491): at android.view.ViewGroup.dispatchWindowFocusChanged(ViewGroup.java:658)
    04-18 20:54:45.319: ERROR/AndroidRuntime(491): at android.view.ViewRoot.handleMessage(ViewRoot.java:1921)
    04-18 20:54:45.319: ERROR/AndroidRuntime(491): at android.os.Handler.dispatchMessage(Handler.java:99)
    04-18 20:54:45.319: ERROR/AndroidRuntime(491): at android.os.Looper.loop(Looper.java:123)
    04-18 20:54:45.319: ERROR/AndroidRuntime(491): at android.app.ActivityThread.main(ActivityThread.java:4627)
    04-18 20:54:45.319: ERROR/AndroidRuntime(491): at java.lang.reflect.Method.invokeNative(Native Method)
    04-18 20:54:45.319: ERROR/AndroidRuntime(491): at java.lang.reflect.Method.invoke(Method.java:521)
    04-18 20:54:45.319: ERROR/AndroidRuntime(491): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
    04-18 20:54:45.319: ERROR/AndroidRuntime(491): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
    04-18 20:54:45.319: ERROR/AndroidRuntime(491): at dalvik.system.NativeStart.main(Native Method)
    04-18 20:54:45.379: WARN/ActivityManager(58): Force finishing activity andengine.test/.AndEngineTestActivity

    ReplyDelete