[Android 게임개발] 메모리 게임 만들기(Building A Memory-Game)!

설명: 게임의 룰은 간단하다. 스테이지가 하나씩 올라갈때 마다 도형이 하나씩 추가 되는데, 이전에 존재했던 도형을 터치하면 fail, 새롭게 생긴 도형을 터치하면 다음 스테이지로 넘어갈수 있게된다. 즉, 한마디로 정리하자면 [기억력 테스트 게임] 정도 되겠다.
이번 프로젝트에서도 activity_main은 사용하지 않는다. 대신 java 파일을 독립적으로 생성하여 MainActivity에서 인플레이트 하는식이다.
(사실 MainActivity에서도 하는거라곤 인플레이트가 ‘전부’다.)
밑의 코드와 함께 설명을 참고하자.

-MyGameView.java

package com.example.mymemorygame;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.NonNull;

import java.util.ArrayList;
import java.util.Random;

public class MyGameView extends View {

    private static final int BLANK = 0; // 상태값 (대기상태)
    private static final int PLAY = 1; //게임이 진행중
    private static final int DELAY = 1500; // 도형 생성 시간 (1500 = 1.5초)

    private  int status;

    private ArrayList<Shape> arShape = new ArrayList<Shape>();
    private Random rnd = new Random(); //난수값을 구하는 클래스
    private Activity mParent;

    public MyGameView(Context context){
        super(context);
        mParent = (Activity)context;
        status = BLANK;

        //핸들러 실행
        handler.sendEmptyMessageDelayed(0,DELAY);
    }

    //도형의 색상, 모양 등의 속성을 관리할수있는 중첩클래스 생성
    class Shape{
        static final int RECT = 0; // 사각형
        static final int CIRCLE = 1; //원
        static final int TRIANGLE = 2; //사각형

        int what;
        int color;
        Rect rt;

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //캔버스를 검정색으로 두자
        canvas.drawColor(Color.BLACK);

        //대기상태 (BLANK(0)) 일때 리턴시키겠다.
        if(status == BLANK){
            return;
        }
        int idx = 0;

        for(idx = 0; idx < arShape.size(); idx++){
            Paint pnt = new Paint();

            pnt.setColor(arShape.get(idx).color);

            Rect rt = arShape.get(idx).rt;

            switch (arShape.get(idx).what){
                case Shape.RECT:
                    canvas.drawRect(rt,pnt);
                    break;
                    case Shape.CIRCLE:
                        canvas.drawCircle
                                (rt.left+rt.width()/2,
                                rt.top+rt.height()/2,
                                rt.width()/2, pnt);
                        break;

                        case Shape.TRIANGLE:
                            Path path = new Path();
                            path.moveTo(rt.left + rt.width()/2,rt.top);
                            path.lineTo(rt.left,rt.bottom);
                            path.lineTo(rt.right,rt.bottom);
                            canvas.drawPath(path,pnt);
                            break;
                            default:
                                break;
            }
        }
    }

    //일정 간격으로 도형을 생성하기 위한 핸들러
    Handler handler = new Handler(){

        @Override
        public void handleMessage(@NonNull Message msg) {

            AddNewShape();
            status = PLAY;
            invalidate();

            String title = "stage ~ " + arShape.size();
            mParent.setTitle(title);
        }
    };

    //새로운 도형을 추가하기 위한 메소드
    public void AddNewShape(){
        Shape shape = new Shape();
        int idx;

        //도형이 중복되었는지 판별하는 변수
        boolean bFindIntersect;

        Rect rt = new Rect();

        //랜덤으로 도형 사이즈 생성 - 50~150 사이의 난수를 생성해서 만든다.
        while (true){

            int size = rnd.nextInt(101)+50;

            //사각형의 범위
            rt.left = rnd.nextInt(getWidth());
            rt.top = rnd.nextInt(getHeight());
            rt.right = rt.left + size;
            rt.bottom = rt.top + size;

            //도형이 화면을 벗어날경우 새로 그리라는 메소드
            if(rt.right >= getWidth() || rt.bottom >= getHeight()){

                // if문 안에있는 케이스가 될 경우 continue 하라, 즉 처음부터 다시해라
                continue;
            }

            bFindIntersect = false;

            for(idx = 0; idx <arShape.size();idx++){
                if(rt.intersect(arShape.get(idx).rt)==true){

                    //겹치면 true
                    bFindIntersect = true;

                }
            }
if(bFindIntersect == false){
    break;
}
        }

        //도형 모양
        shape.what = rnd.nextInt(3);

        //도형 컬러 지정
        switch (rnd.nextInt(5)){
            case 0:
                shape.color = Color.WHITE;
                break;
            case 1:
                shape.color = Color.RED;
                break;
            case 2:
                shape.color = Color.GREEN;
                break;
            case 3:
                shape.color = Color.BLUE;
                break;
            case 4:
                shape.color = Color.MAGENTA;
                break;

        }
        shape.rt = rt;
        arShape.add(shape);

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(event.getAction() == MotionEvent.ACTION_DOWN){

            //도형의 위치를 제대로 클릭했는지 판별하기 위한 메소드 호출
            int sel;

            sel = findShapeIdx((int)event.getX(),(int)event.getY());

            if(sel == -1){
                return true;
            }

            //완료가 되면 다음스테이지로 넘기겠다.
            if(sel == arShape.size() -1){
                status = BLANK;
                invalidate();
                handler.sendEmptyMessageDelayed(0,DELAY);

                //게임 종료시 alertDialog 생성
            }else{
                AlertDialog.Builder builder = new AlertDialog.Builder(mParent);
                builder.setMessage("restart Game~?");
                builder.setTitle("Game Over~");
                builder.setNegativeButton("OK", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {

                        //게임 초기화
                        arShape.clear();
                        status = BLANK;
                        invalidate();
                        handler.sendEmptyMessageDelayed(0,DELAY);

                    }
                });

                builder.setPositiveButton("exit", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        mParent.finish(); // 종료
                    }
                });

                builder.setCancelable(false);
                builder.show();

            }

        }

        return false;
    }

    public int findShapeIdx(int x,int y) {
        for (int idx = 0; idx < arShape.size(); idx++) {

            //arShape에 담긴 도형중에 터치된 x,y좌표를 가진 rect객체의 인덱스값을 반환
            if (arShape.get(idx).rt.contains(x, y)) { // contains? 객체가 있는지 없는지에 따라 명령
                return idx;
            }
        }
        return findShapeIdx(x,y);
    }
}

안드로이드에서의 코드를 그룹화시켰는데 4단계로 정도 나눠보았습니다!

(1) 게임의 모든 이벤트는 하나의 부모 class속 하나의 부모 view에서 일어난다.

public class MyGameView extends View {}

(2) 게임은 총 2가지의 상태값을 가진다: 게임의 대기상태 / 진행상태

 private static final int BLANK = 0; // 상태값 (대기상태)
 private static final int PLAY = 1; //게임이 진행중

(3) 게임은 총 5가지의 기본 멤버변수를 가진다: DELAY / status / ArrayList / Random / Activity

private static final int DELAY = 1500; // 도형 생성 시간 (1500 = 1.5초)
private int status;
private ArrayList<Shape> arShape = new ArrayList<Shape>();
private Random rnd = new Random(); //난수값을 구하는 클래스
private Activity mParent;

(4) 게임은 총 6가지의 상위 객체(메소드)를 가진다: MyGameView / onDraw / Handler / AddNewShape / onTouchEvent / FindShapeIdx

1. public MyGameView(Context context){    }

2. @Override protected void onDraw(Canvas canvas) {    }

3. Handler handler = new Handler(){ public void handleMessage(@NonNull Message msg) {	}}

4. public void AddNewShape(){    }

5. @Override public boolean onTouchEvent(MotionEvent event) {	}

6. public int findShapeIdx(int x,int y) {    }

-MainActivity.java

package com.example.mymemorygame;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        setContentView(R.layout.activity_main);

        setContentView(new MyGameView(this));
    }
}

Main쪽 내용은 코드가 간단합니다! 안드로이드스튜디오나 자바 또는 코틀린에서 만든 MainActivity에서 인플레이트만 해주면 마무리가 된다고 봐야합니다!ㅎㅎ

“[Android 게임개발] 메모리 게임 만들기(Building A Memory-Game)!”에 대한 8개의 생각

  1. Signing up was a breeze and the first deposit bonus matched exactly what was advertised. Games load instantly even during peak hours and the search filter makes it easy to find slots by provider. Had a tiny question about my wagering requirements and got a clear answer right away. Really trustworthy platform overall. loginawin

    답글

댓글 남기기