2013年7月6日土曜日

[ゲーム][Java]キャラクターの動作部分ができたので晒すことにしました

どうも僕です.

ぶっちゃけ4月ぐらいに思い立ったけど忘れててました.

仕様としては
・自分のキャラクターがいる.
→矢印キーで自分のキャラクターを動かせる.
→スペースで弾が撃てる.

・敵キャラクターがいる.
→自動で動く
→なんパターンか動きを入れる.
→弾は撃ってこない.

・パワーアップアイテムがある.

・スコアとか付けたい.

今回は自分のキャラがいるとこまで作り終えたのでコード晒しておきます.

後々敵キャラクターを作ることを考えてキャラクター用のクラスを作って見ました.以下です.

package core.Character;

import java.awt.Graphics;
import java.awt.Image;

import core.MainGame;

//キャラクタークラス
public abstract class Character {
 public static MainGame display;    //画面取得
 protected Image img;     //画像
 protected int x;     //座標
 protected int y;
 protected int w, h;     //幅,高さ
 private boolean dead;     //死亡判定

 //コンストラクタ
 protected Character(Image img){
  this.img = img;     //キャラクターの画像設定
  w = img.getWidth(display);   //画像の幅取得
  h = img.getHeight(display);   //画像の高さ取得
  dead = false;     //死亡判定を生きてるにする
 }

 //死亡判定
 //戻り値 死亡判定
 public boolean isDead(){
  return dead;
 }

 //死にたくなった時
 public void dead(){
  dead = true;
 }

 //移動
 //抽象メソッドとしておく.
 public abstract void move();

 //当たり判定
 //引数 判定したいキャラクター
 //戻り値 当たり:true ハズレ:false
 public boolean checkHit(Character character){
  if(x > character.x - w && x < character.x + character.w
    && y > character.y - h && y < character.y + character.h){
   return true;
  }
  return false;
 }

 //キャラクターの座標取得
 public int getX(){
  return x;
 }
 public int getY(){
  return y;
 }

 //描画
 //引数 描画したいグラフィック
 public void draw(Graphics g){
  g.drawImage(img, x, y, display);
 }
} 

こいつを継承する形で自分のキャラクターを作ります.

package core.Character;

import core.Key;

//自機クラス
public class MyCharacter extends Character{
 private int bulletIntervalCount;    //弾の発射間隔
 private int power;      //弾の強さ
 private int invincible;      //無敵時間
 private static int[][] bulletVecter= {    //弾の向き
  {0,-8},       //前方へ
  {-7,-7, 7, -7},      //左右の斜め前へ
  {-7, -7, 0, -8, 7, -7},     //前方と左右の斜め前へ
  {-7, -7, 0, -8, 7, -7, -7, 7, 7, 7}   //前方と左右の斜め前後へ
 };

 public MyCharacter() {
  super(display.imgMyCharacter);    //imgのセット
  bulletIntervalCount = 0;    //弾の発射間隔を初期化
  power = 0;      //弾の強さの初期化
  invincible = 0;      //無敵時間の初期化
  x = (display.getW() - w) / 2;    //初期座標X
  y = display.getH() - h - 16;    //初期座標Y
 }

 @Override
 public void move() {
  // 左移動
  if (Key.left) {
   x -= 4;
   if (x < 0){
    x = 0;
   }
  }
  // 右移動
  if (Key.right) {
   x += 4;
   if (x > display.getW() - w){
    x = display.getW() - w;
   }
  }
  // 上移動
  if (Key.up) {
   y -= 4;
   if (y < 0){
    y = 0;
   }
  }
  // 下移動
  if (Key.down) {
   y += 4;
   if (y > display.getH() - h){
    y = display.getH() - h;
   }
  }

  //弾の発射処理
  if(bulletIntervalCount > 0){
   bulletIntervalCount--;
  }
  if(Key.space){
   if(bulletIntervalCount <= 0){
    for(int i = 0; i < bulletVecter[power].length; i += 2){
     display.addList(new Bullet(
       x + w / 2,
       y,
       bulletVecter[power][i],
       bulletVecter[power][i + 1])
     );
    }
    bulletIntervalCount = 7;
   }
  }
 }
}

さらっとBulletクラスが出てきたのでそれも晒します.

package core.Character;

//弾クラス
public class Bullet extends Character{
 private int vx, vy;  //速度

 //コンストラクタ
 public Bullet(int x, int y, int vx, int vy){
  super(display.imgBullet);
  this.x = x - w / 2;
  this.y = y - h / 2;
  this.vx = vx;
  this.vy = vy;
 }

 @Override
 public void move(){
  //弾の移動
  x += vx;
  y += vy;

  //画面外に出たとき
  if(x < -w || x > display.getW() || y < -h || y > display.getH()){
   dead();  //消滅
  }
 }

 @Override
 //当たり判定を行わない
 public boolean checkHit(Character character){
  return false;
 }

}

ゲームのメイン画面ができたのでそっちもどうせだから晒しとく.

package core;

import java.applet.Applet;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.MediaTracker;
import java.util.Vector;

import core.Character.Character;
import core.Character.MyCharacter;

@SuppressWarnings("serial")
public class MainGame extends Applet implements Runnable{
 private Vector characterList, characterListTemp;   //キャラクターリスト, 仮キャラクターリスト,htmlタグと読まれるのでエスケープ
 private volatile Thread gameThread;      //ゲーム用スレッド
 private Image offImage;        //オフセット画面
 private Graphics gv;        //グラフィックオブジェクト
 private int scene;        //シーン
 private final int SCENE_INIT = 0;      //シーン:初期化
 private final int SCENE_TITLE = 1;      //シーン:タイトル
 private final int SCENE_MAINGAME = 2;      //シーン:メインゲーム
 private final int SCENE_GAMEOVER = 3;      //シーン:ゲームオーバー
 private int score;        //得点
 private Font scoreFont;        //得点用のフォント
 private int width, height;       //画面幅,高さ
 private MediaTracker mt;       //画像読み込み用
 private int enemyInterval;       //敵出現間隔
 private int enemyIntervalCount;       //敵出現間隔用カウンタ
 private int itemIntervalCount;       //アイテム出現間隔カウンタ
 private MyCharacter myCharacter;      //マイキャラクターオブジェクト保存用
 public Image imgMyCharacter;       //マイキャラクター用の画像
 public Image imgBullet;        //弾用の画像

 public void init(){
  width = getSize().width;      //widthに画面サイズを代入
  height = getSize().height;      //heightに画面サイズを代入

  scene = SCENE_INIT;
  scoreFont = new Font("Arial", Font.BOLD, 12);    //得点用フォント生成
  setBackground(Color.black);      //背景色を黒に設定
  setForeground(Color.white);      //前景色を白に設定
  offImage = createImage(width, height);     //仮想画面の生成
  gv = offImage.getGraphics();
  addKeyListener(new Key());      //キー操作受付オブジェクト生成
  Character.display = this;      //Characterクラスにアプレットを渡す
  mt = new MediaTracker(this);      //メディアトラッカー生成
  characterList = new Vector();    //キャラクターリスト生成,htmlタグと読まれるのでエスケープ
  characterListTemp = new Vector();    //仮キャラクターリスト生成,htmlタグと読まれるのでエスケープ

  //画像読み込み+メディアトラッカーに登録
  imgMyCharacter = getImage(getDocumentBase(), "./data/MyCharacter.gif");
  mt.addImage(imgMyCharacter, 0);
  imgBullet = getImage(getDocumentBase(), "./data/Bullet.gif");
  mt.addImage(imgBullet, 0);
  requestFocus();        //フォーカス要求
 }

 //ゲームスレッドの開始
 public void start() {
  if(gameThread == null) {
   gameThread = new Thread(this);
   gameThread.start();
  }
 }

 //ゲームスレッドの停止
 public void stop() {
  gameThread = null;
 }

 @Override
 public void run() {
  //メインループ
  while (gameThread == Thread.currentThread()) {
   //裏画面の消去
   gv.clearRect(0, 0, width, height);
   //各シーン処理
   switch (scene) {
    case SCENE_INIT: gameInit(); break;   //準備画面
    case SCENE_TITLE: gameTitle(); break;   //タイトル画面
    case SCENE_MAINGAME: gameMain(); break;   //ゲーム画面
    case SCENE_GAMEOVER: gameOver(); break;   //ゲームオーバー画面
   }

   repaint();       //再描画

   //20ミリ秒待つ
   try {
    Thread.sleep(20);
   } catch (InterruptedException e) {
    break;
   }
  }
 }

 //ゲーム初期化処理
 private void gameInit(){
  drawCenter("Loading...", 180);      //画面描画

  //画像が完全に読み込まれるまで待つ
  if (mt.statusAll(true) == MediaTracker.COMPLETE) {
   ready();       //準備処理
   scene = SCENE_TITLE;      //タイトル画面へ移行
  }
 }

 //ゲーム開始準備処理
 private void ready(){
  score = 0;        //得点初期化
  enemyInterval = 50;       //敵出現間隔初期化
  enemyIntervalCount = enemyInterval;     //敵出現間隔カウンタ初期化
  itemIntervalCount = 0;       //アイテム出現間隔カウンタ初期化
  characterList.setSize(0);      //キャラリスト初期化
  characterListTemp.setSize(0);      //仮キャラリスト初期化
  myCharacter = new MyCharacter();     //マイキャラクターの生成
  addList(myCharacter);       //マイキャラクターの追加
 }

 //ゲームタイトル時処理
 private void gameTitle(){
  //エンターキーが押された時
  if(Key.enter){
   System.gc();       //リプレイするときにゴミを消す
   scene = SCENE_MAINGAME;      //ゲーム画面へ移行
  }

  //タイトル画面の描画
  gv.setFont(new Font("Arial",Font.BOLD, 20));    //フォント設定
  drawCenter("GAME START_", 80);      //ゲームスタートを中央表示
  drawCenter("HIT ENTER KEY", 180);     //エンター叩いてを中央表示
 }

 //ゲーム画面処理
 private void gameMain(){
  int i, j;        //計算処理用
  Character character, characterCheck;     //キャラクターの処理及び当たり判定用

  //全キャラクターの移動処理
  for(i = 0; i < characterList.size(); i++){
   character = (Character)(characterList.elementAt(i));  //リストからキャラクターの情報を取得
   character.move();      //それぞれの動きを実行
  }

  //全キャラクターの描画処理
  for(i = 0; i < characterList.size(); i++){
   character = (Character)(characterList.elementAt(i));  //リストからキャラクターの情報を取得
   character.draw(gv);      //それぞれの描画を実行
  }

  //全キャラクターの当たり判定処理
  for(i = 0; i < characterList.size(); i++){
   character = (Character)(characterList.elementAt(i));  //リストからキャラクターの情報を取得
   for(j = 0; j < characterList.size(); j++){
    characterCheck = (Character)(characterList.elementAt(j)); //リストからキャラクターの情報を取得
    character.checkHit(characterCheck);    //当たり判定
   }
  }

  //死亡判定処理
  i = 0;
  while(i < characterList.size()){
   character = (Character)(characterList.elementAt(i));  //リストからキャラクターの情報を取得

   //死亡していたら
   if(character.isDead()){
    characterList.removeElementAt(i);   //リストからキャラクターの削除
   }
   else{
    i++;       //次のキャラクターへ進む
   }
  }

  //キャラクターリストの更新処理
  for(i = 0; i < characterListTemp.size(); i++){
   characterList.addElement(characterListTemp.elementAt(i)); //キャラクターリストに仮キャラクターリストを追加
  }
  characterListTemp.setSize(0);      //仮キャラクターリストを初期化

  //スコアの描画処理
  gv.setFont(scoreFont);       //フォント設定
  gv.drawString("SCORE:" + String.valueOf(score), 10,20);   //スコア表示
 }

 //ゲームオーバー画面処理
 private void gameOver(){
  //エンターキーが押されたら
  if(Key.enter){
   ready();       //ゲーム開始準備
   scene = SCENE_TITLE;      //タイトル画面へ移行
  }

  // ゲームオーバー画面描画
  gv.setFont(new Font("Arial", Font.BOLD, 28));
  drawCenter("GAME OVER", 180);
  drawCenter("SCORE : " + String.valueOf(score), 350);
 }

 //アプレットの幅の取得
 public int getW(){
  return width;
 }

 //アプレットの高さを取得
 public int getH(){
  return height;
 }

 //マイキャラクターの座標取得
 int getMyCharacterX(){
  return myCharacter.getX();
 }
 int getMyCharacterY(){
  return myCharacter.getY();
 }

 //文字列を中央揃えで表示
 private void drawCenter(String str, int y) {
  FontMetrics fm = getFontMetrics(gv.getFont());    //フォント指定
  gv.drawString(str, (width - fm.stringWidth(str)) / 2, y);  //文字出力
 }

 //仮キャラクターリストにキャラクターを追加する
 public void addList(Character character){
  characterListTemp.addElement(character);    //仮キャラクターリストに追加
 }

 //スコア加算
 void addScore(int s){
  score += s;        //スコアに引数だけ加算
 }

 //ゲームオーバーへ移行用
 void goGameOver(){
  scene = SCENE_GAMEOVER;       //ゲームオーバー画面へ移行
 }

 //描画処理
 @Override
 public void paint(Graphics g){
  g.drawImage(offImage, 0,0,this);     //仮想画面から切り替え
 }

 //更新
 public void updata(Graphics g){
  paint(g);        //仮想画面を描画処理へ送る
 }
}

これから敵キャラも出すことを想定して敵の出現要素とかも入れてあります...
あとこの前書いたレンダリング処理入れてないです.あの処理思いついた時には書き終わってしまってたので…次の機会に使います.と言うかもっと綺麗に書くつもりでいたんですけどね...
メインがめちゃくちゃ読みにくい…

前に紹介したキー入力クラスと一緒に適当な画像ぶち込むと動くはず.AppletじゃなくてSwing使えばよかったと後悔しながら続きも作ると思います.

 おかしい処理とかあれば教えてください.

僕から以上.

0 件のコメント:

コメントを投稿