메뉴 건너뛰기

XEDITION

ANDROID

조회 수 93 추천 수 0 댓글 0
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄

제가 지금 쓰고 있는 게임 관련 책(가칭 안드로이드 게임 프로그래밍의 기초)에
예제로 사용한 건데요, 슈팅 게임과 관련이 있을 듯 해서 소스를 올립니다.



프로그램을 시작한 후 화면의 빈 곳을 Touch하면 파란 비눗방울이 새로 만들어져 몽실몽실거리면서 둥둥 떠다니는데  비눗방울을 터치하면 위의 그림처럼 터져서 오색찬란한 작은 비눗방울로 쪼개져
사방으로 흩어져 사라집니다. 또, 큰 비눗방울이 벽과 3회 이상 충돌하면 스스로 터집니다.

이 프로젝트를 작성하기 위해 다음의 이미지가 필요합니다.



                       


Bubble.java  (별도의 Class 파일임)
 


package com.Project4;

import java.util.*;

import android.content.*;
import android.graphics.*;

//-------------------------------------
//  비눗방울
//-------------------------------------
public class Bubble {
     public int x, y, rad;                      // 좌표, 반지름
     public  Bitmap imgBall;                 // 비트맵 이미지
     public  boolean dead = false;        // 터뜨림 여부
 
     private int _rad;                             // 원래의 반지름
     private int sx, sy;                          // 이동 방향 및 속도
     private int width, height;                 // View의 크기
     private Bitmap Bubbles[] = new Bitmap[6]; // 풍선 애니메이션 용 이미지 
     private int imgNum = 0;                  // 이미지 번호
     private int loop = 0;                       // 루프 카운터
     private int counter = 0;                   // 벽과 충돌 회수
 
     //-------------------------------------
     //  생성자
     //-------------------------------------
     public Bubble(Context context, int _x, int _y, int _width, int _height) {
          width = _width;
          height = _height;
          x = _x;
          y = _y;
  
          imgBall = BitmapFactory.decodeResource(context.getResources(), R.drawable.bubble_1);
          Random rnd = new Random();
          _rad = rnd.nextInt(11) + 20;            // 반지름 : 20 ~ 30;
          rad = _rad;
     
          // 반지름이 2 간격으로 커졌자 작아지는 공 6개 만들기
          for (int i = 0; i <= 3; i++)
               Bubbles[i] = Bitmap.createScaledBitmap(imgBall, _rad * 2 + i * 2, _rad * 2 + i * 2, false);
           Bubbles[4] = Bubbles[2];  
           Bubbles[5] = Bubbles[1];
           imgBall = Bubbles[0];
     
          sx = rnd.nextInt(2) == 0 ? -1 : 1;
          sy = rnd.nextInt(2) == 0 ? -2 : 2;
          MoveBubble();
      }
 
      //-------------------------------------
      //  Move
      //-------------------------------------
      public void MoveBubble() {
            loop++;
            if (loop % 3 == 0) {
                  imgNum++;                               // 비눗방울 번호
                  if (imgNum > 5) imgNum = 0;
                  imgBall = Bubbles[imgNum];
   
                  // 비눗방울의 반지름 설정
                  rad = _rad + (imgNum <= 3 ? imgNum : 6 - imgNum) * 2;   
            }
  
            x += sx;
            y += sy;
            if (x <= rad || x >= width - rad) {  // 좌우로 충돌
                  sx = -sx;
                  x += sx;
                  counter++;
            } 

            if (y <= rad || y >= height - rad) { // 상하로 충돌 
                  sy = -sy;
                  y += sy;
                  counter++;
            }
            if (counter >= 3) dead = true;  // 벽과 충돌 횟수
      }

} // Bubble

//------------------------------------------------------------

//-------------------------------------
//  작은 방울
//-------------------------------------
class SmallBall {
      public int x, y, rad;                            // 좌표, 반지름
      public  boolean dead = false;            // 터뜨림 여부
      public  Bitmap imgBall;                       // 비트맵 이미지
 
      private int width, height;                  // View의 크기
      private int cx, cy;                           // 원의 중심점
      private int cr;                                  // 원의 반지름
      private double r;                             // 이동 각도 (radian)
      private int speed;                            // 이동 속도
      private int num;                               // 이미지 번호
      private int life;                                 // 생명 주기
 
      //-------------------------------------
      //  생성자
      //-------------------------------------
      public SmallBall(Context context, int _x, int _y, int ang, int _width, int _height) {
            cx = _x;                            // 중심점
            cy = _y;
            width = _width;
            height = _height;
            r = ang * Math.PI / 180;       // 각도 radian
  
            Random rnd = new Random();
            speed = rnd.nextInt(5) + 2;            // 속도     : 2~6
            rad = rnd.nextInt(6) + 2;                 // 반지름   : 2~7
            num = rnd.nextInt(6);                    // 이미지  : 0~5
            life = rnd.nextInt(31) + 20; // 20~50
  
            imgBall = BitmapFactory.decodeResource(context.getResources(), R.drawable.b0 + num);
            imgBall = Bitmap.createScaledBitmap(imgBall, rad * 2, rad * 2, false);
            cr = 10;      // 원의 초기 반지름
            MoveBall();
      }

      //-------------------------------------
      //  MoveBall
      //-------------------------------------
      public void MoveBall() {
            life--;
            cr += speed;
            x = (int) (cx + Math.cos(r) * cr); 
            y = (int) (cy - Math.sin(r) * cr); 
            if (x < -rad || x > width + rad ||
              y < -rad || y > height + rad || life <= 0)
        dead = true;
      }

} // class



MyGameView.java
 


package com.Project4;

import java.util.*;

import android.content.*;
import android.graphics.*;
import android.util.*;
import android.view.*;
import android.view.SurfaceHolder.Callback;

public class MyGameView extends SurfaceView implements Callback {
      GameThread mThread;
      SurfaceHolder mHolder;

      //-------------------------------------
      //  생성자
      //-------------------------------------
      public MyGameView(Context context, AttributeSet attrs) {
            super(context, attrs);
            SurfaceHolder holder = getHolder();
            holder.addCallback(this);
            mHolder = holder;
            mThread = new GameThread(holder, context);

            setFocusable(true);  // View가 포커스를 받을 수 있도록 설정
     }

     //-------------------------------------
     //  SurfaceView가 생성될 때 실행되는 부분
     //-------------------------------------
    @Override
     public void surfaceCreated(SurfaceHolder holder) {
            mThread.start();
      }

     //-------------------------------------
     //  SurfaceView가 바뀔 때 실행되는 부분
     //-------------------------------------
    @Override
    public void surfaceChanged(SurfaceHolder arg0, int format, int width, int height) {

    }

    //-------------------------------------
    //  SurfaceView가 해제될 때 실행되는 부분
    //-------------------------------------
   @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
           boolean done = true;
           while (done) {
               try {
                   mThread.join();                        // 스레드가 현재 step을 끝낼 때 까지 대기
                   done = false;
             } catch (InterruptedException e) {  // 인터럽트 신호가 오면?   
                 // 그 신호 무시 - 아무것도 않음
             } 
           } // while
    }
 
//----------------------------------------------------------------
 
 //-------------------------------------
 //  GameThread Class
 //-------------------------------------
 class GameThread extends Thread {
     SurfaceHolder mHolder;                                    // SurfaceHolder를 저장할 변수
     Context mContext;
  
     int width, height;
     Bitmap imgBack;
     ArrayList<Bubble> mBall = new ArrayList<Bubble>();              // 큰방울
     ArrayList<SmallBall> sBall = new ArrayList<SmallBall>();         // 작은방울
    
     //-------------------------------------
     //  생성자 
     //-------------------------------------
     public GameThread(SurfaceHolder holder, Context context) {
         mHolder = holder;                                                   // SurfaceHolder 보존
         mContext = context;
   
         Display display = ((WindowManager) context.getSystemService 
                                    (Context.WINDOW_SERVICE)).getDefaultDisplay();
         width = display.getWidth();              // View의 가로 폭
         height = display.getHeight() - 50;     // View의 세로 높이

         imgBack = BitmapFactory.decodeResource(getResources(), R.drawable.sky);
         imgBack = Bitmap.createScaledBitmap(imgBack, width, height, false);
  }
  
  //-------------------------------------
  //  비눗방울 만들기  - Touch Event에서 호출
  //-------------------------------------
  public void MakeBubble(int x, int y) {
      boolean flag = false;
      for (Bubble tmp :  mBall) {
          if (Math.pow(tmp.x - x, 2) + Math.pow(tmp.y - y, 2)  <= Math.pow(tmp.rad, 2)) {
              tmp.dead = true;                   // 비눗방울 Touch일 경우 
              flag = true;  
          }
       }
      if (flag == false)                              // 비눗방울 Touch가 아니면 비눗방울 생성 
       mBall.add(new Bubble(mContext, x, y, width, height));
  }

  //-------------------------------------
  //  작은  비눗방울 만들기
  //-------------------------------------
  private void MakeSmallBall(int x, int y) {
      Random rnd = new Random();
      int count = rnd.nextInt(9) + 7;               // 7~15개
      for (int i = 1; i <= count; i++) {
          int ang = rnd.nextInt(360);   
          sBall.add(new SmallBall(mContext, x, y, ang, width, height));
      }
  }

  //-------------------------------------
  //  비눗방울 이동  - run에서 호출
  //-------------------------------------
  public void MoveBubble() {
      // 큰 비눗방울 이동
      for (int i = mBall.size() - 1; i >= 0; i--) {
          mBall.get(i).MoveBubble();
          if (mBall.get(i).dead == true) {
              // 작은 비눗방울을 만들고 큰 것은 터뜨림
              MakeSmallBall(mBall.get(i).x, mBall.get(i).y); // 작은 방울
              mBall.remove(i);
          }
      }

      // 작은 비눗방울 이동
      for (int i = sBall.size() - 1; i >= 0; i--) {
          sBall.get(i).MoveBall();
          if (sBall.get(i).dead == true)
              sBall.remove(i);
      }
  }
  
  //-------------------------------------
  //  Canvas에 그리기
  //-------------------------------------
  public void run() {
      Canvas canvas = null;      // canvas를 만든다
      while (true) {
          canvas = mHolder.lockCanvas();  // canvas를 잠그고 버퍼 할당
          try {
              synchronized (mHolder) {  // 동기화 유지
                  MoveBubble();
                  canvas.drawBitmap(imgBack, 0, 0, null);

                  // 큰 비눗방울
                  for (Bubble tmp : mBall) {
                      canvas.drawBitmap(tmp.imgBall, tmp.x - tmp.rad,  tmp.y - tmp.rad, null);
                  } 
                  // 작은비눗방울
                  for (SmallBall tmp : sBall) {
                      canvas.drawBitmap(tmp.imgBall, tmp.x - tmp.rad,  tmp.y - tmp.rad, null);
                  } 
                } // sync
             } finally {       // 버퍼 작업이 끝나면 
                if (canvas != null)    // 버퍼의 내용을 View에 전송
                    mHolder.unlockCanvasAndPost(canvas);     
            }
         } // while
     } // run
 
 } // GameThread 끝
 
 //-------------------------------------
 //  onTouch Event
 //-------------------------------------
 @Override
  public boolean onTouchEvent(MotionEvent event) {
           if (event.getAction() == MotionEvent.ACTION_DOWN) {
               synchronized (mHolder) {
                   int x = (int) event.getX(); 
                   int y = (int) event.getY();
                   mThread.MakeBubble(x, y);
               }
           }
           return true;
    }
} // SurfaceView



MainACtivity.java
 


package com.Project4;

import android.app.Activity;
import android.content.*;
import android.os.Bundle;

public class MainActivity extends Activity {
   
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }



main.xml
 

 
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout            
  xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<com.Project4.MyGameView
 android:id="@+id/mGameView"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"/>
</FrameLayout>



자세한 주석을 달아두었으므로 쉽게 이해하시리라 생각됩니다.
Bubble Class 내부에 삼각함수가 나오긴 하는데 별로 어려운 내용은 아니므로 
설명은 Pass합니다.
우주여행 Live Wallpaper의 경우처럼 작은 풍선을 사방으로 멀어지게 하기 위해
sin, cos를 사용했습니다.

지금 생각해 보니까 이걸 Live Wallpaper로 만들어도 괜찮을 것 같다는 생각이 듭니다.
사용자가 View를 터치하지 않더라도 가끔씩 비눗방울이 화면에 나타나면
그걸 손가락으로 톡 쳐서 터뜨리는 것도 재미있을 것 같네요...
심심할 때 가지고 놀게...

Live Wallpaper 만드는 방법은 바로 아래에 올려 놨으니까 누가 요거랑 섞어서 한 번 만들어 보세요...

모두들 즐컴하세요~~

?

List of Articles
번호 제목 글쓴이 날짜 조회 수
» [슈팅 게임 만들기] 11. 강좌가 늦어져 예제로 대신합니다 엘리후 2017.06.01 93
63 안드로이드 게임 만들기 SpriteAnimation 엘리후 2016.02.29 672
62 [안드로이드 어플 개발] 이동 애니메이션 (Translate Animation) 엘리후 2015.12.24 373
61 Multipart를 이용하여 파일+문자열 한꺼번에 전송하기 엘리후 2015.12.01 3140
60 How do I upload large files (10MB) in Android? 엘리후 2015.11.26 925
59 Upload Video from android to server? 엘리후 2015.11.26 266
58 안드로이드] Fragment 쉽게 사용하기 엘리후 2015.11.24 386
57 안드로이드(android) 다이얼로그(dialog) 종류별 구현 방법 엘리후 2015.11.11 431
56 ActionBar에 메뉴 넣기. 엘리후 2015.11.11 188
55 앱 통계를 내 사이트에서 수집하기 엘리후 2015.10.07 827
54 나인패치 (9patch) 이미지 만드는 방법 엘리후 2015.10.07 350
53 VOLLEY 라이브러리 엘리후 2015.10.02 539
52 안드로이드 나인패치 이미지 만들기 [나인패치 영역선택 및 수정] 엘리후 2015.09.25 879
51 안드로이드 Support Library Setup 엘리후 2015.09.25 444
50 안드로이드 Multipart 업로드 예제 엘리후 2015.09.23 8811
49 이클립스/Eclipse 메모리 Heap 영역 늘리기 엘리후 2015.09.13 263
48 [Android, Hybrid]openFileChooser 킷캣에서 동작하지 않는 문제( openFileChooser Kitkat bug ) 엘리후 2015.08.27 605
47 Android – WebView에서 input태그를 통한 파일 업로드하기 엘리후 2015.08.27 3627
46 WebView 컨트롤 엘리후 2015.08.27 445
45 안드로이드 동영상 파일 업로드 예제 엘리후 2015.08.25 1181
44 안드로이드 채팅 ui 만들기 관련 리스트뷰(ListView)의 layout_weight 옵션을 이용한 채팅 GUI 구현, 카카오 채팅 말풍선 엘리후 2015.08.21 27790
43 Android 간단한 로그인, 회원 가입 폼 만들기 for Mac (PHPMyAdmin 이용) 엘리후 2015.08.21 1962
42 안드로이드 프레임워크 프로그래밍 [시스템서비스 추가하기] 엘리후 2015.08.11 156
41 안드로이드에서 실행 시 발생하는 에러 엘리후 2015.08.10 164
40 안드로이드 ; 스크롤 뷰 엘리후 2015.08.09 1840
39 android Facebook SDK 3.0 로그인 및 포스팅 엘리후 2015.08.07 479
38 [android] 안드로이드 전역 변수 사용하기 android global variable 엘리후 2015.07.30 645
37 [Android] WebView를 사용할때 HttpClient를 이용한 Session 유지 엘리후 2015.07.29 5567
36 [MY_SQL] where절과 having절의 차이 엘리후 2015.07.27 246
35 [안드로이드 개발 강좌] 데이터베이스(DataBase) - DB 생성,저장,수정,삭제 엘리후 2015.07.21 939
Board Pagination Prev 1 2 3 Next
/ 3
위로