Hướng dẫn làm game Flappy Bird trên Android dùng engine AndEngine

Bước 1:  Thiết lập môi trường lập trình
1/ Cài đặt JDK (Java Devlopment Kit) dùng để tạo ra môi trường thực thi máy ảo Java
    Download link:
     - 32 bit: https://drive.google.com/file/d/0B8tAQ0_sJKCSSElMSUxUbERTVDg/view?usp=sharing
     - 64bit: https://drive.google.com/file/d/0B8tAQ0_sJKCSUHZTdzlSbzNqMFE/view?usp=sharing
    hoặc download ở: http://www.oracle.com/
2/ Tải bộ ADT (Android Development Tool): Gồm Eclipse bộ công cụ hỗ trợ lập trình android và bộ Android SDK, download ở đây:
    https://drive.google.com/file/d/0B8tAQ0_sJKCSVjN2MUVKcWRhUE0/view?usp=sharing

3/ Thư viện AndEngine:
    download ở đây: https://drive.google.com/file/d/0B8tAQ0_sJKCSVEh1UU4zNzNndUU/view?usp=sharing

4/ Khởi động công cụ eclipse:
    Run eclipse.exe  trong thư mục..\adt-bundle-windows-x86-20140702\eclipse\eclipse.exe
    Tùy chọn Select a workspace, các bạn chọn folder để lưu dự án của mình.

5/ Kiểm tra thư viện SDK android mà eclipse tham chiếu tới.
    - Vào menu trên thanh eclipse Window --> Preferences --> chọn Android, trong ô SDK Location sẽ lưu đường dẫn tới bộ SDK android.
    - Nếu thiếu android-17 thì ta tải thêm hoặc copy bộ API android-17 vào thư mục: ..\adt-bundle-windows-x86-20140702\sdk\platforms
   - Nếu thiếu system-images dùng để tạo máy ảo thì tải thêm hoặc copy vào: \adt-bundle-windows-x86-20140702\sdk\system-images\android-17

6/ Tạo giả lập máy ảo android (gọi làm AVD - Android Virtual Device)
    Trên thanh công cụ trên eclip, chọn icon có hình điện thoai (Android Virtual Device Manager).
    Chọn Create sau đó điền các thông số cho máy ảo.
    AVD name: tên máy ảo (thường đặt theo tên phiên bản)
    Device:        loại máy chọn Nexus S 480 X800
    Target:        chọn phiên bản API ở đây chọn API 17
    CPU/ABI:   Giả lập chip xử lý cho thiết bị ảo (chọn ARM (armeabi-v7a))
    Keyboard:
    Skin:
    Front Camera:
    Back Camera:
    Memory Option:
                     RAM:   512             VM Heap:
    Internal Storage:  200          
    SDCard:
                   Size:        200          
                 
    Emulator Options:
                   Snapshot             Use Host GPU (chọn)

    --> chọn OK, chờ vài phút để máy ảo khởi động xong.

Bước 2: Tạo dự án FlappyBird bằng Eclip trong bộ ADT
              Bước này mới tạo được dự án Hello với tên FlappyBrid.
Vào menu File --> New --> Android Application Project:
             Application Name:             FlappyBird
             Minimun Required SDK: chọn API 17 (version thấp nhất ứng dụng support)
             Target SDK:                       chọn API 17
             Compile With:                    chọn API 17
             Theme:                                None

 Chọn Next ... --> Finish

 Buid và chạy thử:
 Click chuột phải vào project FlappyBird --> chọn Run As --> chọn Android Application, kết quả chạy lên chữ Hello world!.

Bước 3: Copy thư viện andengine.jar
  (trong ..\pvenkat4ever-andengine\lib\andengine.jar) vào thư mục dự án FlappyBird --> libs (hoặc kéo thả vào đây)

Bước 4code mở đầu, cấu hình camera và hiển thị nền game
mở tệp tin src\MainActivity.java

Xóa hết chỉ để lại:
public class MainActivity {

}

Sau đó extends BaseGameActivity và thêm các phương thức mặc định của lớp BaseGameActivity vào--> kết quả:
public class MainActivity extends BaseGameActivity {
       //Cấu hình game: kích thước camera,...
       @Override
       public Engine onLoadEngine() {
              return null;
       }

       //Load các tệp tin tài nguyên: hình ảnh, âm thanh
       @Override
       public void onLoadResources() {//load các
             
       }
      
    //Phương thức hiển thị các đối tượng trong game
       @Override
       public Scene onLoadScene() {
              return null;
       }

       @Override
       public void onLoadComplete() {
             
       }     

}

Tiếp theo sẽ tạo ra màn hình game màu xanh:
public class MainActivity extends BaseGameActivity {
       private static final int CAMERA_WIDTH = 480; //Chiều rộng màn hình
       private static final int CAMERA_HEIGHT = 800; //Chiều cao màn hình
      
       //Cấu hình game: kích thước camera,...
       @Override
       public Engine onLoadEngine() {
              final Camera mcamera = new Camera(0, 0, CAMERA_WIDTHCAMERA_HEIGHT);
              final EngineOptions engineOptions = new EngineOptions(true, ScreenOrientation.PORTRAITnew RatioResolutionPolicy(CAMERA_WIDTHCAMERA_HEIGHT), mcamera);
              return new Engine(engineOptions);
       }

       //Load các tệp tin tài nguyên: hình ảnh, âm thanh
       @Override
       public void onLoadResources() {//load các
             
       }
      
    //Phương thức hiển thị các đối tượng trong game
       @Override
       public Scene onLoadScene() {
              this.mEngine.registerUpdateHandler(new FPSLogger());
              final Scene scene = new Scene(2);
              scene.setBackground(new ColorBackground(0.0f, 0.0f, 1.0f));
             
              return scene;
       }

       @Override
       public void onLoadComplete() {
             
       }     
}

Buid và run kết quả cho ra màn hình game màu xanh.

Để bật các tính năng debug trong eclipse ta vào menu Window --> Show View: chọn ConsoleLogCat, nếu không tìm thấy vào Other.

Bước 5Hiển text in game

Khai báo các biến cho load text
       //Text
       private Texture mFontTexture;

       private Font mFont;

Viết code khởi tạo cho các biến:
       //Load các tệp tin tài nguyên: hình ảnh, âm thanh
       @Override
       public void onLoadResources() {
              //Text
              this.mFontTexture = new Texture(256, 256, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
              this.mFont = new Font(this.mFontTexture, Typeface.create(Typeface.DEFAULT, Typeface.BOLD), 32, true, Color.BLACK);
              this.mEngine.getTextureManager().loadTexture(this.mFontTexture);
              this.mEngine.getFontManager().loadFont(this.mFont);
       }

Viết code draw text ra màn hình:
       //Phương thức hiển thị các đối tượng trong game
       @Override
       public Scene onLoadScene() {
              //...        
              //Draw text to screen
              Text text = new Text(50, 50, mFont, "Welcome to iVietTech");
              scene.attachChild(text);
              Log.i("FlappyBird", "Draw Text");
             
              return scene;
       }

Bước 6: Vẽ background game
Chuẩn bị đồ họa: tạo thư mục image trong thư mục assets ..\FlappyBird\assets\image, copy tất cả các tệp tin hình ảnh của game vào thư mục image. Đồ họa của game download ở đây, link: https://drive.google.com/file/d/0B8tAQ0_sJKCSZmI1MTNIakhsN2s/view?usp=sharing

Khai báo biến load image background
       //Image background
       private TextureRegion mTextRegion_Background;
       private Texture mTexture_Background;

Load hình background:
//Load các tệp tin tài nguyên: hình ảnh, âm thanh
@Override
public void onLoadResources() {
    //Text
      
     
    //Image background
    TextureRegionFactory.setAssetBasePath("image/");
    this.mTexture_Background = new Texture(512, 1024, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
    this.mTextRegion_Background = TextureRegionFactory.createFromAsset(this.mTexture_Background, this, "background.png", 0, 0);
    this.mEngine.getTextureManager().loadTexture(this.mTexture_Background);
}

Vẽ background ra màn hình:
//Phương thức hiển thị các đối tượng trong game
@Override
public Scene onLoadScene() {
       //...        
       //Draw background
       Sprite sprite_Background = new Sprite(0, 0, mTextRegion_Background);
       scene.attachChild(sprite_Background);
             
       //Draw text to screen
       //...        
       return scene;
}


Bước 7Vẽ background di động
Khai báo biến load image background2
       //Image background2
       private TextureRegion mTextRegion_Background2;
       private Texture mTexture_Background2;
       private Sprite sprite_Background2;

Load hình background2:
//Load các tệp tin tài nguyên: hình ảnh, âm thanh
@Override
public void onLoadResources() {
    //Text
            
    //Image background2
    this.mTexture_Background2 = new Texture(1024, 1024, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
    this.mTextRegion_Background2 = TextureRegionFactory.createFromAsset(this.mTexture_Background2this"background2.png", 0, 0);
    this.mEngine.getTextureManager().loadTexture(this.mTexture_Background2);
}

Vẽ background2 ra màn hình:
//Phương thức hiển thị các đối tượng trong game
@Override
public Scene onLoadScene() {
       //...        
       //Draw background2
       sprite_Background2new Sprite(0, 0, mTextRegion_Background2);
       scene.attachChild(sprite_Background2);
             
       //Draw text to screen
       //...        
       return scene;
}

Build chạy thử xem đã load được background2 lên chưa!  Tiếp theo để tạo hiệu ứng background2 động ta thay đổi tọa độ của background2

Tạo vòng lặp trong game, để thay đổi tọa độ background2
Khai báo biến:
private float SPEED = 2;//tốc độ di chuyển background
private float px_bacground2 = 0;//để di chuyển tọa độ x background2

Tạo vòng lặp:
//Phương thức hiển thị các đối tượng trong game
@Override
public Scene onLoadScene() {
     //Draw text to screen
     ...
     //Loop in game
     scene.registerUpdateHandler(new IUpdateHandler() {
            @Override
            public void reset() {
              // TODO Auto-generated method stub
            }
                    
            @Override
            public void onUpdate(float pSecondsElapsed) {
                   px_bacground2 -= SPEED;
                   if (px_bacground2 < -CAMERA_WIDTH) {
                         px_bacground2 = 0;
                   }
                   sprite_Background2.setPosition(px_bacground2, CAMERA_HEIGHT - 80);
            }
     }); 
return scene;
}

Bước 8Vẽ hình động - vẽ chú chim ra màn hình
Khai báo biến:
//Image bird animation
private Texture mTexture_Bird;
private TiledTextureRegion mTiledTextureRegion_Bird;

private AnimatedSprite mAnimationSprite_Bird;

Load tài nguyên:
//Load các tệp tin tài nguyên: hình ảnh, âm thanh
@Override
public void onLoadResources() {
       //Image bird animation
       this.mTexture_Bird = new Texture(2048, 1024, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
       this.mTiledTextureRegion_Bird = TextureRegionFactory.createTiledFromAsset(this.mTexture_Bird, this, "bird_sprite.png", 0, 0, 3, 3);
       this.mEngine.getTextureManager().loadTexture(this.mTexture_Bird);
}

Vẽ ra màn hình:
//Phương thức hiển thị các đối tượng trong game
@Override
public Scene onLoadScene() {
       //Draw background2
       ...   
       //Draw bird animation
       mAnimationSprite_Bird = new AnimatedSprite(CAMERA_WIDTH >> 2, CAMERA_HEIGHT >> 2, mTiledTextureRegion_Bird);
       mAnimationSprite_Bird.setScale(0.25f);
       scene.attachChild(mAnimationSprite_Bird);
       mAnimationSprite_Bird.animate(new long[] {200, 200, 200}, 0, 2, true);
       return scene;
}

Bước 9Vẽ ống nước đi động
Khai báo
//Image water pipe
private TextureRegion mTextureRegion_WaterPipe;
private Texture mTexture_WaterPipe;

private Sprite sprite_WaterPipe;
private float px_WaterPipe = CAMERA_WIDTH *1.5f;
private float py_WaterPipe = 0;
private float WIDTH_WATER_PIPE = 70;

Load tài nguyên:
//Image water pipe
this.mTexture_WaterPipe = new Texture( 1024, 1024,           TextureOptions.BILINEAR_PREMULTIPLYALPHA );
this.mTextureRegion_WaterPipe = new TextureRegionFactory().createFromAsset( this.mTexture_WaterPipe, this, "water_pipe.png", 0, 0);
this.mEngine.getTextureManager().loadTexture(this.mTexture_WaterPipe);

Vẽ ra màn hình: chú ý vẽ ống nước trước background và sau bacgrund2
//Load các tệp tin tài nguyên: hình ảnh, âm thanh
@Override
public void onLoadResources() {
       //...
       //Image water pipe
       this.mTexture_WaterPipe = new Texture(1024, 1024, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
       this.mTextureRegion_WaterPipe = new TextureRegionFactory().createFromAsset(this.mTexture_WaterPipe, this, "water_pipe.png", 0, 0);
       this.mEngine.getTextureManager().loadTexture(this.mTexture_WaterPipe);

}

Cập nhật tọa độ để sprite water pipe di chuyển:
//Loop in game
scene.registerUpdateHandler(new IUpdateHandler() {
       @Override
       public void reset() {
              // TODO Auto-generated method stub
       }
      
       @Override
       public void onUpdate(float pSecondsElapsed) {
              //...
              //Di chuyen water pipe
              px_WaterPipe -= SPEED;
              if (px_WaterPipe < - WIDTH_WATER_PIPE)
              {
                     px_WaterPipe = CAMERA_WIDTH * 1.2f;
              }
                          
              //cập nhật tọa độ cho sprite water pipe
              sprite_Background2.setPosition(px_bacground2, CAMERA_HEIGHT - 80);
              sprite_WaterPipe.setPosition(px_WaterPipe, py_WaterPipe);
       }
});

Tiếp theo làm thêm một ống nước di chuyển nữa, như vậy sẽ có 2 ống nước di chuyển trên màn hình:
Khai báo sprite cho water piper 2:
private Sprite sprite_WaterPipe1;
private float px_WaterPipe1 = CAMERA_WIDTH *1.5f + 300;

private float py_WaterPipe1 = -100;

Đã load tài nguyên cho texture water pipe ở bước trên, ở bước này ta vẽ ra màn hình water pipe2:
//Load các tệp tin tài nguyên: hình ảnh, âm thanh
@Override
public void onLoadResources() {
       //...
       //Draw water pipe2
       sprite_WaterPipe1 = new Sprite(px_WaterPipe1, py_WaterPipe1,
                                                       mTextureRegion_WaterPipe);

       scene.attachChild(sprite_WaterPipe1);
}

Cập nhật tọa độ để sprite water pipe2 di chuyển:
//Loop in game
scene.registerUpdateHandler(new IUpdateHandler() {
       @Override
       public void reset() {
              // TODO Auto-generated method stub
       }
      
       @Override
       public void onUpdate(float pSecondsElapsed) {
              //...
              //Di chuyen water pipe 2
              px_WaterPipe1 -= SPEED;
              if (px_WaterPipe1 < - WIDTH_WATER_PIPE)
              {
                     px_WaterPipe1 = CAMERA_WIDTH * 1.2f;
              }
              
              //Khi ống nước đầu ra giữa màn hình thì cho ống nước thứ 2 xuất hiện phía sau
              if ( (px_WaterPipe < CAMERA_WIDTH / 2) && 
                   (px_WaterPipe > CAMERA_WIDTH / 2 - 2*SPEED)
                  )
              {
                    px_WaterPipe1 = CAMERA_WIDTH + WIDTH_WATER_PIPE;

              }

              //cập nhật tọa độ cho sprite water pipe 2
              sprite_WaterPipe1.setPosition(px_WaterPipe1py_WaterPipe1);
       }
});


Bước 10Viết code touch cho game

public class MainActivity extends BaseGameActivity implements IOnSceneTouchListener {
//Phương thức hiển thị các đối tượng trong game
       @Override
       public Scene onLoadScene() {
//...
              scene.setOnSceneTouchListener(this);
              return scene;
       }
       @Override
       public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {
              switch(pSceneTouchEvent.getAction())
              {
                     case MotionEvent.ACTION_DOWN:
                           Log.i("FlappyBird", "Touch down -------");
                     break;
                     case MotionEvent.ACTION_UP:
                           Log.i("FlappyBird", "Touch up -------");
                     break;
              }
              return false;
       }     
}


Bước 11Viết code xử lý touch chim


Bước 12Xử lý va chạm trong game