
설명: 게임의 룰은 간단하다. 스테이지가 하나씩 올라갈때 마다 도형이 하나씩 추가 되는데, 이전에 존재했던 도형을 터치하면 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에서 인플레이트만 해주면 마무리가 된다고 봐야합니다!ㅎㅎ
Thanks for sharing. I read many of your blog posts, cool, your blog is very good.