//二つのソースから構成されます // //実行には、画像と音声のファイルが必要です //ダウンロードしてコンパイルしてください //************************************* //Applet1.java //*************************************** //シューティングゲーム // //Applet1.java import java.awt.*; import java.awt.event.*; import java.applet.*; //アプレット public class Applet1 extends Applet implements Runnable { private boolean isStandalone = false; Thread aThread = null; Image img; Graphics gra,imgGra; FieldAdmin admin; int Width=300,Height=400;//画面の幅、高さ Label scLabel; //引数値の取得 public String getParameter(String key, String def) { return isStandalone ? System.getProperty(key, def) : (getParameter(key) != null ? getParameter(key) : def); } //アプレットのビルド public Applet1() { } //アプレットの初期化 public void init() { try { jbInit(); } catch(Exception e) { e.printStackTrace(); } inits(); } public void inits() {//ゲーム経過表示用 setLayout(null); scLabel = new Label(" "); add(scLabel); scLabel.reshape(8,0,272,24); } //コンポーネントの初期化 private void jbInit() throws Exception { } //アプレットの情報取得 public String getAppletInfo() { return "アプレット情報"; } //引数情報の取得 public String[][] getParameterInfo() { return null; } public void start() {//アプレット開始で実行 Canvas canvas = new Canvas();//描画用キャンバス add(canvas); canvas.reshape(0,24,Width,Height); gra = canvas.getGraphics();//表示用 //ダブルバッファ利用 img = createImage(Width,Height); imgGra = img.getGraphics();//ここに描画する requestFocus(); if (aThread==null) (aThread = new Thread(this)).start(); } public void run() {//スレッド実行 admin = new FieldAdmin(Width,Height);//全体管理 admin.width = Width; admin.height = Height; //音ファイルを読む admin.aucBeam = getAudioClip( getCodeBase(), "sound/beam.au" ); admin.aucBomb = getAudioClip( getCodeBase(), "sound/bomb.au" ); if(admin.aucBomb==null) System.out.println("fail to load sound"); //画像ファイルを読む admin.imgJiki = getImage( getCodeBase(), "image/im1.gif" ); if(admin.imgJiki==null) System.out.println("fail to load image"); admin.imgEnemy1 = getImage( getCodeBase(), "image/im2.gif" ); admin.imgEnemy2 = getImage( getCodeBase(), "image/im3.gif" ); while(true) { admin.move();//全体移動 imgGra.setColor(Color.black); imgGra.fillRect(0,0,Width,Height);//背景黒 admin.draw(imgGra);//全体表示 gra.drawImage(img,0,0,null);//表示画面へ移す //得点表示 scLabel.setText("Time: "+admin.timer+" , Score: "+admin.score); try { Thread.currentThread().sleep(55);//休止 } catch(InterruptedException exception) {} } } //以下キーの設定 public boolean keyDown(Event ev,int code) {//キー押し if (admin != null) admin.key(code,true); //System.out.println("keydown"); return true; } public boolean keyUp(Event ev,int code) {//キー解除 if (admin != null) admin.key(code,false); return true; } //Main メソッド //mainとして起動する場合利用 public static void main(String[] args) { Applet1 applet = new Applet1(); applet.isStandalone = true; Frame frame; frame = new Frame() { protected void processWindowEvent(WindowEvent e) { super.processWindowEvent(e); if (e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); } } public synchronized void setTitle(String title) { super.setTitle(title); enableEvents(AWTEvent.WINDOW_EVENT_MASK); } }; frame.setTitle("アプレットフレーム"); frame.add(applet, BorderLayout.CENTER); applet.init(); applet.start(); frame.setSize(400,320); Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); frame.setLocation((d.width - frame.getSize().width) / 2, (d.height - frame.getSize().height) / 2); frame.setVisible(true); } } //************************************* //FieldAdmin.java //*************************************** // -------------------------------------------------------- //FieldAdmin.java import java.awt.*; import java.awt.event.*; import java.applet.*; import java.applet.AudioClip; import java.util.*; // Gameの状態などを管理するクラス。 // 敵の出現パターンなどもこのクラス内で記述する。 class FieldAdmin { final static int GAMEOVER = 0;//値変更不可 final static int PLAYING = 1; // Gameモード GAMEOVER, PLAYING のいずれかの値になる。 int mode = GAMEOVER; // Game画面の大きさ int width,height; // Game開始してからのカウンタ int counter=0; // 方向Keyの状態 boolean upKey,downKey,leftKey,rightKey; // Shot keyの状態 int shotKeyState = 0; // 自機のオブジェクト Jiki jiki; // 敵が出現する間隔を制御するカウンター int timer; // 自機が発射するミサイルを管理するリスト ListAdmin missileList; // 敵を管理するリスト ListAdmin enemyList; // 敵弾を管理するリスト ListAdmin enemy2List; // 爆発している敵を管理するリスト ListAdmin bombList; // 画面上を動くオブジェクトすべてを管理するリスト ListAdmin allObject; // スコア int score = 0; // 効果音 AudioClip aucBomb, aucBeam; // 機体画像 Image imgJiki, imgEnemy1, imgEnemy2; // コンストラクタ(生成時に実行) FieldAdmin(int w,int h) { width = w; height = h; init(false); } void init(boolean isStart) { missileList = new ListAdmin(); enemyList = new ListAdmin(); enemy2List = new ListAdmin(); bombList = new ListAdmin(); jiki = new Jiki(this); jiki.x = width / 2; jiki.y = height - 32; timer = 0; allObject = new ListAdmin();//すべてのリストのリスト allObject.add(bombList); if (isStart) allObject.add(jiki); allObject.add(enemy2List); allObject.add(enemyList); allObject.add(missileList); jiki.isAlive = isStart;//開始 if (isStart){ mode = PLAYING; score = 0;//得点 } } // 敵を出現させたり、キャラを動かしたりする void move() { if (mode == PLAYING) timer++;//時刻を進める if (jiki.isKilled()) mode = GAMEOVER;//自機が破壊したら終わり counter--;//これが負になると敵を生成 if (timer < 200) {//時刻200以下なら if (counter <= 0) { Enemy e = new Enemy1(this);//攻撃機を作成 enemyList.add(e); counter = 8; } } else if (timer < 400) {//時刻400以下なら if (counter <= 0) { Enemy e = new Enemy2(this);//爆撃機を作成 enemyList.add(e); counter = 8; } } else if (timer < 800) {//800以下なら if (counter <= 0) { Enemy m; if (Math.random() > 0.25)//乱数が0.25以上なら m = new Enemy1(this); else m = new Enemy2(this); enemyList.add(m); counter = 5; } } else { if (counter <= 0) { Enemy m; if (Math.random() > (double)(timer%100)/100 ) m = new Enemy1(this); else m = new Enemy2(this); enemyList.add(m); counter = 4000/timer>0 ? 4000/timer : 1; } } // キャラを動かす allObject.move(); } // キャラを表示する void draw(Graphics g) { allObject.draw(g);//すべてのキャラを表示 if ( mode == GAMEOVER ) { g.setColor(Color.white); g.drawString("Push [S] key!!",width/2-48,height/2); } } void key(int code,boolean flag) {//アプレットで呼び出し switch(code) {//キー入力を設定 case 1004: upKey = flag; break; case 1005: downKey = flag; break; case 1006: leftKey = flag; break; case 1007: rightKey = flag; break; case 32://space key if (!flag) shotKeyState = 0; else if (shotKeyState == 0) shotKeyState = 1; break; } if (jiki.isKilled() && flag && code == 115)//終了状態でs keyが押されたら init(true); } boolean isInField(MoveObject obj) {//フィールドの中にいるか? return obj.x >= 0 && obj.x < width && obj.y >= 0 && obj.y < height; } boolean isInField(int x,int y) { return x >= 0 && x < width && y >= 0 && y < height; } } // -------------------------------------------------------- // リストの要素 // Field上を動き回る物体や物体グループを抽象化したクラス class ListElement { ListElement next = null;//次の要素 ListElement prev = null;//前の要素 ListAdmin parent = null;//親のリスト // このオブジェクをリストからはずす。 void kill() {//リストから削除 //prevは前、nextは次の要素、 if (parent == null) return; prev.next = next; next.prev = prev; parent = null; } // 死んだら 親をnullに boolean isKilled() { return parent == null; } // 動かす void move() {} // 表示する void draw(Graphics g) {} // あたり判定 MoveObject hitTest(MoveObject obj) { return null; } }//ListElement // -------------------------------------------------- // Listを管理するクラス // 動く物体をグループ化して管理するためのクラス class ListAdmin extends ListElement { //head:先頭 tail:最後を指示  ListElement head = new ListElement(),tail = new ListElement(); ListAdmin() { clear(); } void add(ListElement obj) { //リストの最後にobjを追加する if (obj.parent != null) obj.kill(); obj.parent = this; obj.prev = tail.prev; obj.next = tail; obj.prev.next = obj; tail.prev = obj; } void clear() {//リストをクリアする head.next = tail; tail.prev = head; } // リスト内の要素すべての描画をおこなう。 void draw(Graphics g) { ListElement obj; obj = head.next; while( obj != tail ) { obj.draw(g); obj = obj.next; } } // リスト内の要素すべてを動かす void move() { ListElement obj; obj = head.next; while( obj != tail ) { obj.move(); obj = obj.next; } } // リスト内の要素で、objと衝突しているオブジェクトを1つ返す。 MoveObject hitTest(MoveObject mobj) { ListElement obj = head.next; MoveObject retObj; while(obj != tail) { retObj = obj.hitTest(mobj); if (retObj != null) return retObj; obj = obj.next; } return null; } } // -------------------------------------------------- // 動く物体 class MoveObject extends ListElement { // オブジェクトの位置 int x,y; // 幅と高さ。あたり判定に使う。 int width,height; // あたり判定の対象にするかのフラグ。 boolean isAlive = true; FieldAdmin admin; MoveObject(FieldAdmin a) { admin = a; } // あたり判定 MoveObject hitTest(MoveObject mobj) { if (mobj == null) return null; if (!isAlive || !mobj.isAlive) return null; if (mobj.x - width < x && x < mobj.x + mobj.width && mobj.y - height < y && y < mobj.y + mobj.height) return this; return null; } void addDamage(int d) {} } // -------------------------------------------------- // 敵 class Enemy extends MoveObject { Enemy(FieldAdmin a) {//生成時に実行 super(a);//super()は親のクラス y = 0; x = (int)(Math.random() * a.width);//最初のx座標 } void setPosition(int x,int y) {//位置を設定 this.x = x; this.y = y; } } // -------------------------------------------------- // 自機 class Jiki extends MoveObject { int speed; int counter; Jiki(FieldAdmin a) {//生成時に実行 super(a);//親のコンストラクタを実行,adminを設定 width = 8;//MoveObjectで定義、継承 height = 16; speed = 8;//移動速度 } void move() { if ( !isAlive ) return; //キーで移動 if (admin.upKey && admin.isInField(x,y-speed)) y -= speed; if (admin.downKey && admin.isInField(x,y+speed)) y += speed; if (admin.leftKey && admin.isInField(x-speed,y)) x -= speed; if (admin.rightKey && admin.isInField(x+speed,y)) x += speed; if (admin.shotKeyState == 1) {//発射状態 //admin.shotKeyState = 2; Missile m = new Missile(x+width/2,y-4,admin);//ミサイル生成 admin.missileList.add(m); admin.aucBeam.play();//発射音 if( admin.score>0 ) admin.score--; } } void addDamage(int d) {//破壊された //現在dは利用していない(被爆即破壊) isAlive = false; counter = 0; admin.bombList.add(new Bomb(x+width/2,y+height/2,Color.red,admin)); } void draw(Graphics g) {//自機を表示 if ( !isAlive ) { counter++; if (counter > 16) kill();//16コマ後削除 return; } //g.setColor(Color.cyan); //g.fillRect(x,y,width,height); g.drawImage( admin.imgJiki, x, y, null ); } } // -------------------------------------------------- // 自機が発射するミサイル class Missile extends MoveObject { int speed; Missile(int x,int y,FieldAdmin a) { super(a); width = 2; height = 16; this.x = x; this.y = y; speed = 16; } void move() {//移動処理 y -= speed; MoveObject obj = admin.enemyList.hitTest(this);//衝突判定 if (obj != null) { obj.addDamage(1);//攻撃する kill();//破壊する } else if ( !admin.isInField(this) ) kill();//外に出たら消滅 } void draw(Graphics g) {//ミサイル描画 g.setColor(Color.yellow); g.fillRect(x,y,width,height); } } // -------------------------------------------------- // 爆発 // class Bomb extends MoveObject { int counter; Color color; static Color defaultColor = new Color(128,128,0); Bomb(int x,int y,FieldAdmin a) {//爆発生成 super(a);//親のコンストラクタを実行(adminを設定) counter = 0; color = defaultColor; this.x = x; this.y = y; admin.score += 20;//爆発させたら得点 } Bomb(int x,int y,Color c,FieldAdmin a) {//色指定の爆発 super(a); counter = 0; color = c; this.x = x; this.y = y; } void draw(Graphics g) {//爆発表示 // 爆発を表示 counter += 2; g.setColor(color); g.fillOval(x-counter*2,y-counter*2,counter*4,counter*4); if (counter > 16) kill();//8回表示したら消滅 } } // -------------------------------------------------- // 敵弾 class Tama extends Enemy { int vx,vy; Tama(FieldAdmin a) { super(a); width = 4;//MoveObjectで定義、継承 height = 4; } void setDirection(int speed,boolean isRandom) { double vx2,vy2,r; vx2 = admin.jiki.x - x; //自機へ方向指定 vy2 = admin.jiki.y - y; r = vx2*vx2 + vy2*vy2; if (r < 32*32) {//至近距離では狙わない vx2 = -vx2; vy2 = -vy2; } else if (isRandom) {//乱数で方向 vx2 = Math.random() - 0.5; vy2 = Math.random() - 0.5; r = vx2*vx2 + vy2*vy2; if (r < 0.01) { vx2 = 0; vy2 = 1; r = 1; } } r = speed / Math.sqrt(r); vx = (int)(r * vx2); vy = (int)(r * vy2); } void move() {//移動 x += vx; y += vy; if ( hitTest(admin.jiki) != null ) admin.jiki.addDamage(1);//1だけ攻撃 if ( !admin.isInField(this) ) kill();//外に出たら消滅 } void draw(Graphics g) { g.setColor(Color.white); g.drawOval(x,y,width,height);//楕円表示 } } // -------------------------------------------------- // 敵1 攻撃機  // たまは出してこない。 class Enemy1 extends Enemy { int vx,vy; int life = 3;//寿命 static Color color[] = { new Color(255,128,0), new Color(240,160,0), new Color(200,200,64) }; Enemy1(FieldAdmin a) { super(a); width = 16; height = 16; vx = 2 + (int)(Math.random()*6); if (Math.random() > 0.5) vx = -vx; vy = 12; } void move() { x += vx; y += vy; if (hitTest(admin.jiki) != null) { // 自機と衝突た場合 admin.jiki.addDamage(2);//2だけ攻撃 kill(); return; } // 画面の外にでたら消滅 if (y >= admin.height) kill(); } void draw(Graphics g) { //g.setColor(color[life-1]); //g.fillOval(x,y,width,height); g.drawImage( admin.imgEnemy1, x, y, null ); } void addDamage(int d) {//被爆 life -= d;//残り寿命を減らす if (life <= 0) {//負になったら kill(); admin.bombList.add(new Bomb(x+width/2,y+height/2,admin));//爆発リストに加える admin.aucBomb.play();//爆発音 } } } // -------------------------------------------------- // 敵2:爆撃機 // 左右に動かない敵。たまを出してくる。 class Enemy2 extends Enemy { static Color color = new Color(255,64,128); int counter; Enemy2(FieldAdmin a) { super(a); width = 16; height = 16; counter = (int)(Math.random()*40+8); } void move() { y += 4;//移動 counter--; if (counter <= 0) { // たまを発射する処理 //counter = (int)(Math.random()*40+8); counter = (int)(Math.random()*100+8); Tama t = new Tama(admin); t.setPosition(x,y); t.setDirection(8,Math.random() > 0.75); admin.enemy2List.add(t); } if (hitTest(admin.jiki) != null) { // 自機と衝突したとき admin.jiki.addDamage(2); kill(); return; } if (y >= admin.height) kill(); } void draw(Graphics g) {//表示 //g.setColor(Color.red); //g.fillOval(x,y,width,height); g.drawImage( admin.imgEnemy2, x, y, null ); } void addDamage(int d) {//被爆 kill(); admin.bombList.add(new Bomb(x+width/2,y+height/2,admin)); admin.aucBomb.play(); } }