본문 바로가기

카테고리 없음

Android Game example, Direction of Missile

Android Game example, Direction of Missile

적기가 아군기를 향해 미사일을 발사하면 발사 방향으로 미사일을 회전하고 아군기를 향해 날아간다.

벡터와 삼각함수를 사용하는 예

GameState.java (float형을 지원하기위해 int를 float로 변경한 부분이 있음)

package game.framework;


import java.util.ArrayList;

import java.util.Random;

import android.graphics.*;

import android.util.*;

import android.view.KeyEvent;

import android.view.MotionEvent;


public class GameState implements IState {


Background background;

BackgroundClouds clouds;

Airplane airplane;

GameKeyPad keyPad;


Random rd;

ArrayList<EnemyPlane> enemyList;

long enemyCreationTime;

ExplosionPool expPool;

public GameState() {

keyPad = new GameKeyPad();

airplane = new Airplane();

airplane.init();

airplane.inuse = true;


background = new Background();

clouds = new BackgroundClouds();

expPool = ExplosionPool.getInstance();

rd = new Random();

enemyList = AppManager.getInstance().enemyList;

enemyCreationTime = System.currentTimeMillis()-3000;

}


@Override

public void init() {   }


@Override

public void destroy() { }

@Override

public void update() {

createEnemy();

background.update();

clouds.update();

airplane.update();

EnemyMisPool.getInstance().update();

for(int i=0;i<AppManager.getInstance().enemyList.size();i++) {

if(!(AppManager.getInstance().enemyList.get(i).inuse)) continue;

AppManager.getInstance().enemyList.get(i).update();

}

collisionCheck();

collisionCheck2();

ExplosionPool.getInstance().update();

}


@Override

public void render(Canvas canvas) {

background.draw(canvas);

clouds.draw(canvas);

airplane.draw(canvas);

EnemyMisPool.getInstance().draw(canvas);

for(int i=0;i<AppManager.getInstance().enemyList.size();i++) {

if(!AppManager.getInstance().enemyList.get(i).inuse) continue;

AppManager.getInstance().enemyList.get(i).draw(canvas);

}

ExplosionPool.getInstance().draw(canvas);

keyPad.draw(canvas);

}


/* 아군의 미사일과 적기의 충돌검사 */

public void collisionCheck() {

ArrayList<EnemyPlane> enemyList = AppManager.getInstance().enemyList;

ArrayList<Missile> misList = airplane.misList;

int enemyCnt  = enemyList.size();

for(int i=0;i<misList.size();i++) {

if(!misList.get(i).inuse) continue;

for(int k=0;k<enemyCnt;k++) {

if(!enemyList.get(k).inuse) continue;

if(misList.get(i).rect.intersect(enemyList.get(k).rect)) {

// 충돌탐지/충돌위치에 폭발애니 출력/적기, 미사일 안보이게 처리

SpriteAnimation explosion = expPool.get();

explosion.setPosition((float)(misList.get(i).x-explosion.spriteWidth/2),

(float)(misList.get(i).y-explosion.spriteHeight/2-20));

misList.get(i).inuse = false;

enemyList.get(k).inuse = false;

//isCollided = true;

break;

}

}

}

}


/* 적기의 미사일과 아군기의 충돌검사 */

public void collisionCheck2() {

if(!airplane.inuse)return;

ArrayList<EnemyPlane> enemyList = AppManager.getInstance().enemyList;

for(int i=0;i<enemyList.size();i++) {

if(!enemyList.get(i).inuse) continue;

ArrayList<Missile> misList = EnemyMisPool.getInstance().pool;

for(int k=0;k<misList.size();k++) {

if(!misList.get(k).inuse) continue;

if(misList.get(k).rect.intersect(airplane.rect)) {

Log.i("충돌검사", "아군기 피격");

/* 충돌탐지/충돌위치에 폭발애니 출력/비행기, 미사일 안보이게 처리 */

SpriteAnimation explosion = expPool.get();

explosion.setPosition(airplane.x, airplane.y);

misList.get(k).inuse = false;

airplane.inuse = false;

//isCollided = true;

break;

}

}

}

}


public void createEnemy(){

long currentTime = System.currentTimeMillis();

if((currentTime-enemyCreationTime)<3000) return;

enemyCreationTime = currentTime;

if(rd.nextInt(2)==0) {

int len = enemyList.size();

EnemyPlane ep = null;

for(int i=0;i<len;i++){

if(!enemyList.get(i).inuse){

ep = enemyList.get(i);

Log.i("createEnemy", "재활용");

break;

}

}

if(ep==null){

ep = new EnemyPlane();

Log.i("createEnemy", "적기 새로생성");

}

ep.setPosition(0, 0);

ep.inuse = true;

}

}


@Override

public boolean onKeyDown(int keyCode, KeyEvent event) {

return false;

}


@Override

public boolean onTouchEvent(MotionEvent event) {

return keyPad.onTouchEvent(event);

}

}


Airplane.java

package game.framework;


import java.util.*;

import android.graphics.*;

import android.util.Log;


public class Airplane extends GraphicObject {


int speedX, speedY;

ArrayList<Missile> misList =  new ArrayList<Missile>();

Rect rect = new Rect();

boolean inuse;


public Airplane() {

super(AppManager.getInstance().getBitmap(R.drawable.mustang_transparent));

}


public Airplane(Bitmap bitmap) {

super(bitmap);

}


public void init() {

AppManager.getInstance().setAirplane(this);

}


public void setSpeed(int speedX, int speedY) {

this.speedX = speedX;

this.speedY = speedY;

}


public void update() {

if(inuse) {

if(this.x==0 && this.y==0) return;

x += speedX;

y += speedY;

rect.left = (int)x;

rect.top = (int)y;

rect.bottom = bitmap.getHeight()+(int)y;

rect.right = bitmap.getWidth()+(int)x;

}

for(int i=0;i<misList.size();i++) {

if(misList.get(i).inuse) misList.get(i).update();

}

}


public void draw(Canvas canvas) {

if(inuse) {

if(this.x==0 && this.y==0) {

this.x = canvas.getWidth()/2-bitmap.getWidth()/2;

this.y = canvas.getHeight()-bitmap.getHeight();

}

}

for(int i=0;i<misList.size();i++) {

if(misList.get(i).inuse) misList.get(i).draw(canvas);

}

if(!inuse) return;

canvas.drawBitmap(bitmap, x, y, null);

}


public void fireMissile() {

Missile misToFire = null;

for(int i=0;i<misList.size();i++) {

if( ! misList.get(i).inuse) {

misToFire = misList.get(i);

//Log.i("미사일선택", "미사일 재사용");

break;

}

}

if(misToFire==null) {

misToFire = new Missile();

//Log.i("미사일선택", "미사일 새로 생성");

misList.add(misToFire);

}


misToFire.setPosition(this.x+(bitmap.getWidth()/2)-(misToFire.bitmap.getWidth()/2), this.y);

misToFire.setSpeed(0, -4);

misToFire.inuse = true;

}

}


EnemyPlane.java

package game.framework;


import java.util.*;

import android.graphics.Canvas;

import android.util.Log;


public class EnemyPlane extends Airplane {


long prevFireTime;

Random rd;

public EnemyPlane() {

super(AppManager.getInstance().getBitmap(R.drawable.enemy_transparent));

setSpeed(0, 2);

AppManager.getInstance().enemyList.add(this);


prevFireTime = System.currentTimeMillis();

rd = new Random();

y = 0;

}


@Override

public void update() {

if(inuse) {

if(this.x==0 && this.y==0) return;

x += speedX;

y += speedY;

rect.left = (int)x;

rect.top = (int)y;

rect.bottom = bitmap.getHeight()+(int)y;

rect.right = bitmap.getWidth()+(int)x;

fireMissile();

}

}


@Override

public void draw(Canvas canvas) {

if(inuse) {

if(this.x==0 && this.y==0) {

this.x = rd.nextInt(canvas.getWidth()-bitmap.getWidth());

this.y = -(bitmap.getHeight());

}

}


if(!inuse) return;

if(y>canvas.getHeight()) inuse = false;

canvas.drawBitmap(bitmap, x, y, null);

//Log.i("적기", "draw()");

}


@Override

public void fireMissile() {

long currentTime = System.currentTimeMillis();

if(!(currentTime-prevFireTime > 3000) ) return;

if(rd.nextInt(30)!=0) return;

Missile misToFire = null;

//EnemyMisPool.getInstance().pool;

for(int i=0;i<EnemyMisPool.getInstance().pool.size();i++) {

if( ! EnemyMisPool.getInstance().pool.get(i).inuse) {

misToFire = EnemyMisPool.getInstance().pool.get(i);

Log.i("미사일선택", "미사일 재사용");

break; 

}

}

if(misToFire==null) { 

misToFire = new Missile(AppManager.getInstance().getBitmap(R.drawable.enemy_missile));

Log.i("미사일선택", "미사일 새로 생성");

EnemyMisPool.getInstance().pool.add(misToFire);

}


misToFire.setPosition(this.x+(bitmap.getWidth()/2)-(misToFire.bitmap.getWidth()/2), this.y);

Vector2D misVec = missileVector();

//Log.i("미사일 사선벡터", ""+misVec.x+","+misVec.y);

misToFire.setSpeed(misVec.x, misVec.y);

misToFire.inuse = true;

prevFireTime = currentTime;

}

private Vector2D missileVector(){

Airplane ap = AppManager.getInstance().getAirplane();

Vector2D apVec = new Vector2D(ap.x, ap.y);

Vector2D eneVec = new Vector2D(this.x, this.y);

return apVec.sub(eneVec).normalize().mul(4);

}

}


Missile.java

package game.framework;




import android.graphics.Bitmap;

import android.graphics.Matrix;


import android.graphics.Canvas;


import android.graphics.Rect;


public class Missile extends GraphicObject {


float speedX, speedY;

int bitmapHeight;

boolean inuse;

Rect rect = new Rect();

public Missile() {

super(AppManager.getInstance().getBitmap(R.drawable.missile));

bitmapHeight = bitmap.getHeight();

}

public Missile(Bitmap bitmap) {

super(bitmap);

bitmapHeight = bitmap.getHeight();

}


public void setSpeed(float speedX, float speedY) {

this.speedX = speedX;

this.speedY = speedY;

}

public void update() {

if(!inuse) return;

x += this.speedX;

y += this.speedY;

rect.left = (int)x;

rect.top = (int)y;

rect.bottom = bitmap.getHeight()+(int)y;

rect.right = bitmap.getWidth()+(int)x;

if(y< -bitmapHeight) {

inuse = false;

}

}


public void draw(Canvas canvas) {

if(!inuse) return;

if(y>canvas.getHeight()) inuse = false;

canvas.save();

double ang = Math.atan2(speedY, speedX);

double deg = ang*180/Math.PI;

canvas.rotate((float)deg-90, x, y);

canvas.drawBitmap(bitmap, x, y, null);

canvas.restore();

}

}


GraphicObject.java

package game.framework;


import android.graphics.*;


public class GraphicObject {


protected Bitmap bitmap;

protected float x, y;


public GraphicObject(Bitmap bitmap) {

this.bitmap = bitmap;

}


public void setPosition(float x, float y){

this.x = x;

this.y = y;

}


public void draw(Canvas canvas){

canvas.drawBitmap(bitmap, x, y, null);

}

}


SpriteAnimation.java

package game.framework;


import android.graphics.*;

import android.util.Log;


public class SpriteAnimation extends GraphicObject {


Rect srcRect = new Rect();

Rect dstRect = new Rect();

int msPerFrame;

int cntOfFrames;

int currentFrame;

long prevTime;

int spriteWidth;

int spriteHeight;

int spriteRows, spriteCols;

Paint paint;

public SpriteAnimation(Bitmap bitmap) {

super(bitmap);

}

public void initSpriteData(int rows, int cols, int width, int height, int fps, int cntOfFrames) {

spriteRows = rows;

spriteCols = cols;

spriteWidth = width;

spriteHeight = height;

srcRect.top = 0;

srcRect.bottom = spriteHeight;

srcRect.left = 0;

srcRect.right = spriteWidth;

msPerFrame = 1000/fps;

this.cntOfFrames = cntOfFrames;

paint = new Paint();

paint.setAlpha(255);

}


@Override

public void draw(Canvas canvas) {

dstRect.left = (int)this.x;

dstRect.top = (int)this.y;

dstRect.right = (int)this.x+spriteWidth;

dstRect.bottom = (int)this.y+spriteHeight;

canvas.drawBitmap(bitmap, srcRect, dstRect, paint);

//Log.i("스프라이트 애니", "draw()호출됨");

}

public void update(long currentTime) {

if(currentTime > prevTime + msPerFrame) {

prevTime = currentTime;

currentFrame++;

if(currentFrame >= cntOfFrames) {

currentFrame = 0;

}

}


// 다중행의 Sprite sheet에 대응하기 위해 추가된 부분

srcRect.left = currentFrame%spriteCols*spriteWidth;

srcRect.right = srcRect.left + spriteWidth;

int rowNum = currentFrame/spriteCols;

srcRect.top = rowNum*spriteHeight;

srcRect.bottom = srcRect.top + spriteHeight;

//Log.i("스프라이트 애니", "update()호출됨");

}

}


Vector2D.java

package game.framework;


import android.graphics.PointF;


public class Vector2D extends PointF {


public Vector2D(float x, float y) {

super(x,y);

}

public Vector2D sub(Vector2D vec) {

return new Vector2D(this.x - vec.x, this.y - vec.y);

}

public float dot(Vector2D vec) {

return x*vec.x + y*vec.y;

}

public Vector2D normalize(){

return new Vector2D(x/length(), y/length());

}

public Vector2D mul(float scalar) {

return new Vector2D(x*scalar, y*scalar);

}

}