このドキュメントは http://edu.net.c.dendai.ac.jp/ 上で公開されています。
package jp.ac.dendai.c.jtp.shootingsample;
import android.graphics.Point;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Display;
public class MainActivity extends ActionBarActivity {
private View view;
private Thread mainThread;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onStart(){
super.onStart();
Display display = getWindowManager().getDefaultDisplay();
Point p = new Point();
display.getSize(p);
view = new View(this, p);
view.init();
setContentView(view);
}
@Override
public void onResume(){
super.onResume();
view.start();
}
@Override
public void onStop(){
super.onStop();
while (mainThread != null && mainThread.isAlive()) {
try {
view.shutdown = true;
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
}
package jp.ac.dendai.c.jtp.shootingsample;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Point;
import android.view.MotionEvent;
import android.view.SurfaceView;
import jp.ac.dendai.c.jtp.shootingsample.mono.Haikei;
import jp.ac.dendai.c.jtp.shootingsample.mono.Anata;
import jp.ac.dendai.c.jtp.shootingsample.mono.Mikata;
import jp.ac.dendai.c.jtp.shootingsample.mono.Mono;
import jp.ac.dendai.c.jtp.shootingsample.mono.Shootable;
public class View extends SurfaceView {
final static long tic = 10;
public volatile boolean shutdown;
private int width;
private int height;
private DrawList drawList;
private Mikata mikata;
private HanteiList<Mono> tekiList;
private HanteiList<Shootable> tamaList;
private Context context;
private DrawThread drawThread;
private MoveThread moveThread;
private Score score;
private TekiLogic tekiLogic;
private final Object lock;
public View(Context context, Point p) {
super(context);
this.context = context;
width = p.x;
height = p.y;
lock = new Object();
}
public void init() {
drawList = new DrawList();
score = new Score();
drawList.addScore(score);
drawList.add(new Haikei(context));
tamaList = new HanteiList<>();
mikata = new Anata(context, tamaList);
mikata.set(width / 2, height * 3 / 4);
drawList.add(mikata);
tekiList = new HanteiList<>();
tekiLogic = new TekiLogic(context, tekiList);
drawList.addList(tekiList);
drawList.addList(tamaList);
destroyThread(drawThread);
destroyThread(moveThread);
drawThread = new DrawThread();
moveThread = new MoveThread();
}
public void start(){
shutdown = false;
drawThread.start();
moveThread.start();
}
private void destroyThread(Thread t) {
if (t != null) {
shutdown = true;
while (t.isAlive()) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
}
}
}
private void draw() {
synchronized (lock) {
Canvas canvas = getHolder().lockCanvas();
if (canvas == null) return;
drawList.draw(canvas);
getHolder().unlockCanvasAndPost(canvas); // 描画を終了
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mikata.setDirection(event, width, height);
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
mikata.stop();
if (shutdown) {
init();
start();
}
performClick();
break;
}
return true;
}
@Override
public boolean performClick() {
super.performClick();
return true;
}
class MoveThread extends Thread {
@Override
public void run() {
double previous = (double) System.currentTimeMillis();
double now;
while (!shutdown) {
Debug.append("tamasize", "" + tamaList.size());
synchronized (lock) {
now = System.currentTimeMillis();
double tstep = (now - previous) / tic;
// Debug.append("tstep",""+tstep);
drawList.step(tstep, width, height);
tekiLogic.step(tstep, width, height);
}
previous = now;
for (Shootable s : tamaList) {
Mono m = tekiList.atari(s.getRect());
if (m != null) {
score.add(m.getScore());
s.dead();
m.dead();
}
}
synchronized (lock) {
drawList.update();
}
if (tekiList.atari(mikata.getRect()) != null) {
drawList.stop();
shutdown = true;
break;
}
try {
sleep((long) tic);
} catch (InterruptedException e) {
shutdown = true;
}
}
}
}
class DrawThread extends Thread {
@Override
public void run() {
while (!shutdown) {
draw();
try {
sleep(10);
} catch (InterruptedException e) {
shutdown = true;
}
}
}
}
}
package jp.ac.dendai.c.jtp.shootingsample;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import java.util.HashMap;
import java.util.Map;
public class Debug {
private static boolean mode = false;
private static Map<String, String> pr;
private static Paint paint = new Paint();
private Debug() {
init();
}
public static void draw(Canvas canvas) {
if (mode) return;
if (pr == null || canvas == null) return;
paint.setColor(Color.WHITE);
canvas.drawText(pr.toString(), 0, 100, paint);
}
public static void init() {
pr = new HashMap<>();
}
public static void append(String key, String value) {
if (pr == null) init();
pr.put(key, value);
}
}
package jp.ac.dendai.c.jtp.shootingsample;
import android.graphics.Canvas;
import android.graphics.Color;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import jp.ac.dendai.c.jtp.shootingsample.mono.Mono;
public class DrawList extends ArrayList<Mono> {
private static final long serialVersionUID = 6330699380650402372L;
private Score score;
private List<HanteiList<? extends Mono>> list;
public DrawList() {
super();
list = new ArrayList<>();
}
public void addScore(Score s) {
score = s;
}
public void addList(HanteiList<? extends Mono> list) {
this.list.add(list);
}
public void draw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
score.draw(canvas);
for (Mono m : this) {
m.draw(canvas);
}
for (HanteiList<? extends Mono> l : list) {
for (Mono m : l) {
m.draw(canvas);
}
}
Debug.draw(canvas);
}
public void step(double t, int width, int height) {
for (Mono m : this) {
m.step(t, width, height);
}
for (HanteiList<? extends Mono> l : list) {
for (Mono m : l) {
m.step(t, width, height);
}
}
}
public void stop() {
for (Mono m : this) {
m.stop();
}
for (HanteiList<? extends Mono> l : list) {
for (Mono m : l) {
m.stop();
}
}
}
public void update() {
Iterator<Mono> i = this.iterator();
while (i.hasNext()) {
if (!i.next().isAlive()) {
i.remove();
}
}
Iterator<HanteiList<? extends Mono>> j = list.iterator();
while (j.hasNext()) {
Iterator<? extends Mono> k = j.next().iterator();
while (k.hasNext()) {
if (!k.next().isAlive()) {
k.remove();
}
}
}
}
}
package jp.ac.dendai.c.jtp.shootingsample;
import android.graphics.Rect;
import java.util.ArrayList;
import jp.ac.dendai.c.jtp.shootingsample.mono.Mono;
public class HanteiList<E extends Mono> extends ArrayList<E> {
private static final long serialVersionUID = -3775499867397182898L;
public HanteiList() {
super();
}
public E atari(Rect r) {
for (E m : this) {
if (r.intersect(m.getRect())) return m;
}
return null;
}
}
package jp.ac.dendai.c.jtp.shootingsample;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
public class Score {
private int score;
private Paint paint = new Paint();
public Score() {
paint.setColor(Color.WHITE);
}
public void decrease(int point) {
score -= point;
}
public void draw(Canvas canvas) {
String sc = "000000000" + score;
canvas.drawText(sc.substring(sc.length() - 10), 0, paint.getTextSize(), paint);
}
public void add(int point) {
score += point;
}
}
package jp.ac.dendai.c.jtp.shootingsample;
import java.util.ArrayList;
import jp.ac.dendai.c.jtp.shootingsample.mono.Mono;
public class TamaList extends ArrayList<Mono> {
public TamaList() {
super();
}
}
package jp.ac.dendai.c.jtp.shootingsample;
import android.content.Context;
import jp.ac.dendai.c.jtp.shootingsample.mono.Mono;
import jp.ac.dendai.c.jtp.shootingsample.mono.Teki;
public class TekiLogic {
private static double period = 1000;
private final Context context;
private final HanteiList<Mono> list;
private double tic;
public TekiLogic(Context context, HanteiList<Mono> list) {
this.context = context;
this.list = list;
tic = 0;
list.add(createTeki());
}
private Mono createTeki() {
return new Teki(context, 200, 30);
}
public void step(double tstep, int width, int height) {
tic += tstep;
while (tic > period) {
list.add(createTeki());
tic -= period;
}
}
}
package jp.ac.dendai.c.jtp.shootingsample;
import android.graphics.Rect;
public class Vect {
private double x;
private double y;
public Vect(double x, double y) {
super();
this.x = x;
this.y = y;
}
public Vect() {
}
public void set(double x, double y) {
this.x = x;
this.y = y;
}
public void set(Vect v) {
this.x = v.x;
this.y = v.y;
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
public int getIntX() {
return (int) x;
}
public int getIntY() {
return (int) y;
}
public void add(Vect delta) {
x += delta.x;
y += delta.y;
}
public void add(double t, Vect delta) {
x += delta.x * t;
y += delta.y * t;
}
public void restrict(double c) {
x = x < -c ? -c : x > c ? c : x;
y = y < -c ? -c : y > c ? c : y;
}
public void reflectX() {
x = -x;
}
public void reflectY() {
y = -y;
}
public Vect front(Rect r) {
Vect v = new Vect();
v.set(this);
v.setY(y - r.height());
Debug.append("tamaheight", "" + r.height());
return v;
}
}
package jp.ac.dendai.c.jtp.shootingsample.mono;
import android.graphics.Canvas;
import android.graphics.Rect;
public interface Mono {
void set(int i, int j);
int getScore();
void draw(Canvas canvas);
void stop();
void move(int width, int height);
void step(double t, int width, int height);
Rect getRect();
boolean intersect(Mono m);
boolean isAlive();
void dead();
double getInterval();
void setRect();
}
package jp.ac.dendai.c.jtp.shootingsample.mono;
import jp.ac.dendai.c.jtp.shootingsample.Vect;
public interface Shootable extends Mono {
Shootable getInstance();
void init(Vect p, Vect dp);
}
package jp.ac.dendai.c.jtp.shootingsample.mono;
import jp.ac.dendai.c.jtp.shootingsample.Vect;
public interface Shooter extends Mono {
void shoot(Vect dp);
}
package jp.ac.dendai.c.jtp.shootingsample.mono;
import android.view.MotionEvent;
public interface Mikata extends Shooter {
void setDirection(MotionEvent event, int width, int height);
}
package jp.ac.dendai.c.jtp.shootingsample.mono;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
import jp.ac.dendai.c.jtp.shootingsample.Vect;
public abstract class AbstractMono implements Mono {
protected final int width;
protected final int height;
protected Vect p;
protected Vect dp;
protected double period;
protected Rect rect;
protected boolean alive;
private int cycle;
private int clock;
private Bitmap[] gazou;
public AbstractMono(Context context, int[] ids) {
p = new Vect();
dp = new Vect();
rect = new Rect();
cycle = ids.length;
gazou = new Bitmap[cycle];
for (int i = 0; i < cycle; i++) {
gazou[i] = BitmapFactory.decodeResource(context.getResources(), ids[i]);
}
width = gazou[0].getWidth();
height = gazou[0].getHeight();
clock = 0;
period = 0;
alive = true;
}
@Override
public void set(int i, int j) {
p.set(i, j);
}
@Override
public int getScore() {
return 0;
}
@Override
public void draw(Canvas canvas) {
int delta = (int) (period / getInterval());
clock = (clock + delta) % cycle;
period -= delta * getInterval();
canvas.drawBitmap(gazou[clock], p.getIntX(), p.getIntY(), null);
}
@Override
public double getInterval() {
return 1;
}
@Override
public void step(double t, int width, int height) {
period += t;
p.add(t, dp);
move(width, height);
setRect();
}
@Override
public void stop() {
dp.set(0, 0);
}
@Override
public void setRect() {
rect.set(p.getIntX(),
p.getIntY(),
p.getIntX() + width,
p.getIntY() + height);
}
@Override
public Rect getRect() {
return rect;
}
@Override
public boolean intersect(Mono m) {
return rect.intersect(m.getRect());
}
@Override
public boolean isAlive() {
return alive;
}
@Override
public void dead() {
alive = false;
}
}
package jp.ac.dendai.c.jtp.shootingsample.mono;
import android.content.Context;
import jp.ac.dendai.c.jtp.shootingsample.Vect;
public abstract class AbstractShootable extends AbstractMono implements Shootable {
public AbstractShootable(Context context, int[] ids) {
super(context, ids);
}
@Override
public void init(Vect p, Vect dp) {
this.p.set(p.getX(), p.getY());
this.dp.set(dp.getX(), dp.getY());
setRect();
}
}
package jp.ac.dendai.c.jtp.shootingsample.mono;
import android.content.Context;
import jp.ac.dendai.c.jtp.shootingsample.HanteiList;
import jp.ac.dendai.c.jtp.shootingsample.Vect;
public abstract class AbstractShooter extends AbstractMono implements Shooter {
private HanteiList<Shootable> list;
private Shootable tama;
public AbstractShooter(Context context, int[] ids, HanteiList<Shootable> list, Shootable tama) {
super(context, ids);
this.list = list;
this.tama = tama;
tama.setRect();
}
@Override
public void shoot(Vect dp) {
Shootable newtama = tama.getInstance();
newtama.init(p.front(tama.getRect()), dp);
list.add(newtama);
}
}
package jp.ac.dendai.c.jtp.shootingsample.mono;
import jp.ac.dendai.c.jtp.shootingsample.R;
import jp.ac.dendai.c.jtp.shootingsample.Vect;
import android.content.Context;
import android.graphics.Canvas;
public class Haikei extends AbstractMono {
private static final int[] ids = {R.drawable.haikei};
public Haikei(Context context){
super(context,ids);
p.set(0,12);
}
@Override
public void move(int width, int height) {
}
}
package jp.ac.dendai.c.jtp.shootingsample.mono;
import android.content.Context;
import android.view.MotionEvent;
import jp.ac.dendai.c.jtp.shootingsample.Debug;
import jp.ac.dendai.c.jtp.shootingsample.HanteiList;
import jp.ac.dendai.c.jtp.shootingsample.R;
import jp.ac.dendai.c.jtp.shootingsample.Vect;
public class Anata extends AbstractShooter implements Mikata {
private static final int[] ids = {R.drawable.anata};
private final static double shootperiod = 500;
private static final Vect tamadp = new Vect(0, -1);
private double shoottic;
public Anata(Context context, HanteiList<Shootable> tamalist) {
super(context, ids, tamalist, new Tama(context));
shoottic = 0;
}
@Override
public void move(int width, int height) {
if (p.getX() > width) p.setX(width);
if (p.getX() < -this.width) p.setX(0);
}
@Override
public void setDirection(MotionEvent event, int width, int height) {
final double delta = 1;
float px = event.getX();
Debug.append("position", "" + width + " " + px);
if (px < width / 2) {
dp.setX(-delta);
} else {
dp.setX(delta);
}
}
@Override
public void step(double t, int width, int height) {
super.step(t, width, height);
shoottic += t;
while (shoottic > shootperiod) {
shoot(tamadp);
shoottic -= shootperiod;
}
}
}
package jp.ac.dendai.c.jtp.shootingsample.mono;
import android.content.Context;
import jp.ac.dendai.c.jtp.shootingsample.R;
public class Haikei extends AbstractMono {
private static final int[] ids = {R.drawable.haikei};
public Haikei(Context context) {
super(context, ids);
p.set(0, 12);
}
@Override
public void move(int width, int height) {
}
}
package jp.ac.dendai.c.jtp.shootingsample.mono;
import android.content.Context;
import jp.ac.dendai.c.jtp.shootingsample.R;
public class Tama extends AbstractShootable {
private static final int[] ids = {R.drawable.tama1, R.drawable.tama2};
private Context context;
public Tama(Context context) {
super(context, ids);
this.context = context;
dp.set(0, -1);
}
@Override
public void move(int width, int height) {
if (p.getX() > width) alive = false;
else if (p.getX() < -this.width) alive = false;
if (p.getY() > height) alive = false;
else if (p.getY() < -this.height) alive = false;
}
@Override
public Shootable getInstance() {
return new Tama(context);
}
@Override
public double getInterval() {
return 10;
}
}
package jp.ac.dendai.c.jtp.shootingsample.mono;
import android.content.Context;
import jp.ac.dendai.c.jtp.shootingsample.R;
import jp.ac.dendai.c.jtp.shootingsample.Vect;
public class Teki extends AbstractMono {
private static final int[] ids = {R.drawable.teki1, R.drawable.teki2};
private int dpindex;
private Vect[] dps = {new Vect(1, 1), new Vect(-1, 1)};
private double dpcycle = 1000;
private double dpcounter;
public Teki(Context context) {
super(context, ids);
}
public Teki(Context context, int x, int y) {
super(context, ids);
set(x, y);
dp.set(dps[0]);
dpindex = 0;
dpcounter = 0;
}
@Override
public void move(int width, int height) {
if (p.getX() > width) p.setX(-this.width);
else if (p.getX() < -this.width) p.setX(width);
if (p.getY() > height) p.setY(-this.height);
else if (p.getY() < -this.height) p.setY(height);
}
@Override
public double getInterval() {
return 23;
}
@Override
public int getScore() {
return 100;
}
@Override
public void step(double t, int width, int height) {
period += t;
if (dpcounter + t > dpcycle) {
p.add(dpcycle - dpcounter, dps[dpindex]);
dpindex = (dpindex + 1) % dps.length;
dpcounter = dpcounter + t - dpcycle;
p.add(dpcounter, dps[dpindex]);
} else {
p.add(t, dps[dpindex]);
dpcounter += t;
}
move(width, height);
setRect();
}
}
後述のファイルの他、res/drawable-hdpi フォルダ内に下記の画像ファイルを 入れる