안녕하세요. 호랑인 입니다.
오늘은 Slide 라고, 제가 KTX 기다리는 동안 심심해서 만들기 시작해서 KTX 내에서 갖고 논, 아주아주 간단한 (게임이라고 부르기도 애매한) 앱을 만든 과정을 소개시켜드리려고 합니다.
사실 과정의 대부분은 안드로이드에서 게임을 개발할 때마다 매번 반복해야 하는 과정이기 때문에, 어려워 보이더라도, 우선 맥락을 이해하는데에 초점을 두고 보시기 바랍니다.
우선, Slide 는 빨간색 사각형이 움직이면서 모서리에 맞으면 튕겨지는, 매우 간단한 게임입니다. 바로 시작해봅시다.
안드로이드 스튜디오 (Android Studio) 에서 새로운 프로젝트를 시작하시고, Empty Activity 를 선택하시기 바랍니다.
그리고 아래와 같이 바꾸시면 됩니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package // 자신만의 package 정보를 넣어주시면 됩니다.; import android.app.Activity; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.Window; import android.view.WindowManager; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); this.requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(new GameView(this)); } } | cs |
중점을 두셔야 할 부분을 말씀드리겠습니다.
1. MainActivity 가 이제 Activity 에서 상속 받습니다.
2. getWindow() ... 부분은 잘 보시면 전체화면으로 설정하는 부분입니다.
3. this.requestWindowFeature ... 부분은 앱을 만들어보시면 알겠지만, 항상 제일 상단에 파란색(색상은 변경 가능) 띠가 있어, 앱의 이름을 적어놓는다는 것을 볼 수 있습니다. 게임을 할 때에는 이것이 필요가 없으므로, NO_TITLE 을 사용하는 것입니다.
4. setContextView ... 부분은 이제 게임 화면을 만들기 위한 부분입니다. 당장은 GameView 라는 class 를 정의한 적이 없으므로, 빨간 줄이 뜰 것입니다.
한 가지 팁을 드리자면, 위에 있는 import 부분은 굳이 옮기지 않으셔도 됩니다. import 부분을 제외하고 복사 붙여넣기를 하시면, 알아서 Android Studio가 import 를 해줄 것입니다. GameView 는 새로운 class 를 만들어야 하는 것이기 때문에 어쩔 수가 없고요.
이제 GameView 라는 java file을 만들어봅시다. MainActivity.java과 같은 폴더 안에 생성하시면 됩니다. superclass 를 미리 설정해주시고 싶다면, GameView의 superclass 는 android.view.SurfaceView 이고, interface는 android.view.SurfaceHolder.Callback 을 넣으시면 됩니다.
그리고 MainThread 라는 java flie도 만듭시다. 다만, MainThread는 superclass 가 Thread (java.lang) 입니다.
이 둘읭 역할은 사뭇 당연한데, MainThread는 다음 화면을 만드는데에 필요한 계산과 화면을 그리는 부분은 담당합니다. 그리고 결과적으로 GameView class 안에 있는 draw() 라는 함수를 실행시킵니다. 그러면 GameView 의 화면이 갱신되게 되고, 이는 MainActivity 에서 setContextView 로 설정해 놓은 것이기 때문에, 저희 화면에 출력되게 됩니다.
GameView 에 들어가, 아래의 것들을 모두 넣읍시다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | private MainThread thread; public GameView(Context context) { super(context); getHolder().addCallback(this); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceCreated(SurfaceHolder holder) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { } | cs |
1. 우선, MainThread를 하나 만들었습니다. 이것을 통해 thread 에 대한 접근을 할 것입니다.
2. getHolder().addCallback(this) 는 이 화면에서 Event 처리를 가능하게 해주는 부분입니다.
3. 나머지는 Override 하는 부분입니다. 많이 해보셨을 것이라 생각합니다. 우선 당장은 해줄 것이 없습니다. 조금 있다가 surfaceDestroyed 는 고쳐야 하는데, 파괴된 이후에는 thread가 더이상 계산을 진행할 필요가 없기 때문에, 이를 꺼줘야 하기 때문입니다.
이제 가장 중요한, thread 를 만들어야 합니다. 우선, 완성본을 보여드리겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | import android.graphics.Canvas; import android.view.SurfaceHolder; public class MainThread extends Thread { private SurfaceHolder surfaceholder; private GameView gameview; private boolean running; public static Canvas canvas; public int VIEW_WIDTH; public int VIEW_HEIGHT; public MainThread (SurfaceHolder surfaceholder, GameView gameview){ super(); this.surfaceholder = surfaceholder; this.gameview = gameview; this.VIEW_WIDTH = 0; this.VIEW_HEIGHT = 0; } @Override public void run(){ while(running){ canvas = null; try{ canvas = this.surfaceholder.lockCanvas(); this.VIEW_WIDTH = canvas.getWidth(); this.VIEW_HEIGHT = canvas.getHeight(); synchronized (surfaceholder){ this.gameview.update(this.VIEW_WIDTH, this.VIEW_HEIGHT); this.gameview.draw(canvas); } } catch (Exception e) {} finally{ if (canvas != null){ try{ surfaceholder.unlockCanvasAndPost(canvas); }catch(Exception e){ e.printStackTrace(); } } } } } public void setRunning(boolean isRunning){ running = isRunning; } } | cs |
import를 무시하시고요, 제일 위에 package... 부분은 본래 있는 그대로 놔두세요.
천천히 뜯어 봅시다.
1. super(); 상위 클라스인 Thread의 생성 함수를 실행합니다.
2. surfaceholder 와 gameview object를 하나씩 선언했고요, VIEW_HEIGHT 는 나중에 필요해서 그냥 제 마음대로 선언한 겁니다.
3. 보면, 제일 밑에 setRunning 이라는 아이가 있습니다. 이것이 위에서 말했듯이, surfaceCreated 나 surfaceDestroyed 에서 사용될 예정입니다. thread를 끄고 켜는 부분이죠. running 이라는 bool 변수가 이를 조절합니다.
4. run() 이라는 커다란 함수가 보입니다. 아마 threading 보신 적이 있으신 분들은, 예상외로 별 얘기는 없다는 것을 아실 수 있을 것입니다.
5. while (running), 즉 running 변수가 열심히 일하는 것을 볼 수 있죠.
6. 다음에 있는 try 문이 사실 상 전부입니다. 우선, canvas는 lock 되어 있지 않으면, 그곳에 새로운 것을 그릴 수가 없습니다. 따라서, lockCanvas를 하고, 이 변수를 미리 null 로 초기화되어 있는 canvas에 저장합니다. VIEW_WIDTH 는 canvas.getWidth() 로 얻을 수 있습니다.
7. synchronized 가 있는데, 이는 한번에 오직 하나의 thread만 이 작업을 수행할 수 있다는 것입니다. 예를 들어 두개의 thread가 이를 관할하고 있는데, 한번에 하나라는 규칙을 주지 않으면, 같은 작업을 두번 하여 다음 작업이 뛰어 넘어가는 등의 일이 일어날 수 있습니다. 예를 들어, 두명이서 땅을 파고 있다 해봅시다. 그들에게 주어진 것은 (i 번째 땅을 파고), (i 에 1을 더해라) 라 하면, 1번 사람이 1번째 땅을 다 파는 동안에 2번이 2번째 땅을 다 못 팠으면, i 는 아직도 2이기 때문에 1번 사람도 2번째 땅을 파기 시작한다는 것입니다.
8. synchronized 안에 가장 중요한 문장인 update() 와 draw() 를 실행시키는 구문이 있습니다. 이것은 당장은 빨간 줄이 뜰 것입니다. 아직 만들지 않았기 때문이죠. 이제 바로 다시 만들러 갈 것입니다.
9. 그 뒤에 catch 부분은 제대로 실행되지 않았을 때 처리하는 부분입니다. 이들은 저 함수 이름을 google 에 치면 자세한 설명이 나오니, 참고하시기 바랍니다. 당장은 그리 중요하지 않아 넘어가도록 하겠습니다.
다시 GameView 로 돌아갑시다. 그리고 아래와 같이 고칠 것입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | public GameView(Context context) { super(context); getHolder().addCallback(this); thread = new MainThread(getHolder(), this); setFocusable(true); } @Override public void surfaceCreated(SurfaceHolder holder) { thread.setRunning(true); thread.start(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { boolean retry = true; while (retry) { try { thread.setRunning(false); thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } retry = false; } } public void update(int height, int width) { } public void draw(){ } | cs |
1. 우선, thread 를 만들었습니다.
2. 말했던 데로, SurfaceCreated 되면, thread를 돌립니다.
3. surfaceDestroyed 부분은 thread를 멈추는 부분입니다. 멈추지 않으면, 계속 멈추는 겁니다.
4. update와 draw를 만들었습니다. update는 VIEW_HEIGHT와 WIDTH 가 들어올 수 있게, int input 두개를 넣었습니다.
이제 실행 시켜보세요. 검은 화면이 나올겁니다. 혹시 그렇지 않다면, 댓글 부탁드립니다.
축하드립니다! 이제 당신은 모든 종류의 Game 을 만들 수 있게 되었습니다. 이 다음 과정부터는 코딩 능력보다도 창의력이 중요합니다. 다음 게시글로 찾아뵙겠습니다.
'프로젝트 > 프로젝트 [Slide]' 카테고리의 다른 글
[Slide] 세상에서 가장 간단한 안드로이드 게임 - 그리기 (1) | 2018.08.10 |
---|