アルゴリズムなど考え方的なものは、さほど大きい違いはないと思い、質問させていただきました。
現在、マリオのような横スクロールアクションゲームを作成しているのですが。
キー入力や、ジャンプなどの実装も出来て、マリオ(プレイヤー)にショットを打たせる事も出来たのですが
敵キャラにショットを打たせることができません。
というのも、敵キャラとブロック(壁)の描画方法が、テキストファイル内に「B」ならば、ブロック「A]ならば、クリボー(敵)といった方法で描画しているため
マリオと全く一緒の方法では、出来なくなっていて、躓いてます^^;
一応、自分がサンプルを基に、作成したソースを、張っておきますので、よろしければ、どなたか分かるかた
教えていただけると、ありがたいです。
----------------------(MainPanelクラス)----------------------------
package Start;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Iterator;
import java.util.LinkedList;
import javax.swing.JPanel;
import Map.Map;
import Enemy.*;
import Player.*;
import Bullet.AllCharShot;;
public class MainPanel extends JPanel implements Runnable, KeyListener {
// パネルサイズ
public static int WIDTH = 690;
public static int HEIGHT = 490;
private boolean DEBUG_MODE = true;
private Map map;
private Player player;
protected ActionKey 左;
protected ActionKey 右;
protected ActionKey 上;
protected ActionKey スペース;
//クリボーショットステータス
private Kuribo kuribo;
// private Kshot[] Ks;
private Atock atock;
// ゲームループ用スレッド
protected Thread gameLoop;
/*パネルの推奨サイズを設定pack()する時に必要
* setPreferredSize(new Dimension(幅,高))
*
* パネルがキー入力を受け付けるようにする
* setFocusable(true)
*
* ActionKeyクラスを作成
* Mapクラスを作成(最初に読み込むマップ名)
* Playerクラスを作成(x座標,y座標,読み込む画像,参照先)
*
* パネルにキーイベントリスナーを登録
* ゲームループを開始
*
* */
public MainPanel() {
setFocusable(true);
setPreferredSize(new Dimension(WIDTH, HEIGHT));
左 = new ActionKey();
右 = new ActionKey();
上 = new ActionKey(ActionKey.DETECT_INITIAL_PRESS_ONLY);
スペース = new ActionKey();
map = new Map("map.dat");
player = new Player(32, 1088, "Player.png", map);
kuribo = new Kuribo(map.tilesToPixels(getX()), map.tilesToPixels(getY()), "Kuribo.gif", map);
addKeyListener(this);
setLayout(null);
gameLoop = new Thread(this);
gameLoop.start();
// atock.start();
}
public void ゲームオーバー() {
map = new Map("map.dat");
player = new Player(32, 1088, "Playert", map);
}
/*
* キーによってのアクション設定
* ジャンプだけ別にif文を作成しているのは
* 一緒にしてしまうと、ジャンプ中(上が押されてるとき)に左右移動が出来なくなってしまうため
*
* player.isCollsionこのメソッドから下(}まで)はプレイヤーとキャラクター(敵)との
* 当たり判定処理内容を記述している。
* if ((int)player.getY() < (int)kuribo.getY())これにより、上から敵を踏んだ場合
* 敵は、消えるというようにしている。
*
* 正確には、敵よりも、Y座標が高ければいいので真上からでなくてもいい
* 左右と下から、当たった場合は、プレイヤーが死んで、ゲームオーバーという処理になっている。
* */
public void run() {
while (true) {
if (スペース.isPressed())player.Pショット();
if (上.isPressed()) player.ジャンプ();
if (左.isPressed()) { player.左へ(); }
else if (右.isPressed()) { player.右へ(); }
else { player.停止(); }
player.update();
LinkedList sprites = map.getSprites();
Iterator iterator = sprites.iterator();
while (iterator.hasNext()) {
CharObject sprite = (CharObject)iterator.next();
sprite.update();
/*
* プレイヤーのショットに敵が当たった時の判定処理
* */
// for (int i = 0; i < NUM_SHOT; i++) {
// if(pshot[i].isCollision(sprite)){
//
// if (sprite instanceof Kuribo) { // クリボー
// Kuribo kuribo = (Kuribo)sprite;
// kuribo.ショットダメージ();
// ShotCount ++;
// if(0 >= kuribo.現在のHP()){
// iterator.remove();
// }
// pshot[i].store();
// break;
// }
// }
// }
/*
* プレイヤーと衝突した時の判定
* */
if (player.isCollision(sprite)) {
if (sprite instanceof Kuribo) {
Kuribo kuribo = (Kuribo)sprite;
if ((int)player.getY() < (int)kuribo.getY()) {
sprites.remove(kuribo);
player.setForceJump(true);
player.ジャンプ();
break;
} else {
ゲームオーバー();
}
}
}
}
// 再描画
repaint();
// 休止
try { Thread.sleep(20);}
catch (InterruptedException e) {e.printStackTrace();}
}
}
/*
* 描画処理内容
*
* 背景を黒で塗りつぶす
*
* x,y方向へのオフセットを設定している
* これを、やる事により、マップの端では、スクロールしないようにする
*
* マップの描画
* プレイヤーの描画
* キャラオブジェクトを継承したクラスの描画.取得
*
* デバックモードの登録
* */
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(0, 0, getWidth(), getHeight());
int offsetX = MainPanel.WIDTH / 2 - (int)player.getX();
offsetX = Math.min(offsetX, 0);
offsetX = Math.max(offsetX, MainPanel.WIDTH - map.getWidth());
int offsetY = MainPanel.HEIGHT / 2 - (int)player.getY();
offsetY = Math.min(offsetY, 0);
offsetY = Math.max(offsetY, MainPanel.HEIGHT - map.getHeight());
map.draw(g, offsetX, offsetY);
player.draw(g, offsetX, offsetY);
kuribo.draw(g, map.tilesToPixels((offsetX)),map.tilesToPixels(offsetY));
if (DEBUG_MODE) {
Font font = new Font("SansSerif", Font.BOLD, 16);
g.setFont(font);
g.setColor(Color.YELLOW);
g.drawString("プレイヤー座標"+"(" + player.getX() + "," + player.getY() + ")", 32, 15);
g.drawString("HP"+map.tilesToPixels(getX()), 32, 45);
}
LinkedList sprites = map.getSprites();
Iterator iterator = sprites.iterator();
while (iterator.hasNext()) {
CharObject sprite = (CharObject)iterator.next();
sprite.draw(g, offsetX, offsetY);
}
}
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT) 左.press();
if (key == KeyEvent.VK_RIGHT) 右.press();
if (key == KeyEvent.VK_UP) 上.press();
if (key == KeyEvent.VK_SPACE) スペース.press();
}
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT) 左.release();
if (key == KeyEvent.VK_RIGHT) 右.release();
if (key == KeyEvent.VK_UP) 上.release();
if (key == KeyEvent.VK_SPACE) スペース.release();
}
public void keyTyped(KeyEvent e) {
}
}
---------------------------------(Mapクラス)----------------------------
package Map;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.LinkedList;
import javax.swing.ImageIcon;
import Enemy.CharObject;
import Enemy.Kuribo;
import Start.MainPanel;
public class Map {
// タイルサイズ
public static final int TILE_SIZE = 32;
// 重力
public static final double GRAVITY = 0.6;
// マップ
private char[][] map;
// 行数
private int row;
// 列数
private int col;
// 幅
private int width;
// 高さ
private int height;
// ブロックの画像
private Image blockImage;
// スプライトリスト
private LinkedList sprites;
public Map(String filename) {
sprites = new LinkedList();
// マップをロードする
load(filename);
width = TILE_SIZE * col;
height = TILE_SIZE * row;
// イメージをロードする
loadImage();
}
//マップを描画する g 描画オブジェクト offsetX X方向オフセット offsetY Y方向オフセット
public void draw(Graphics g, int offsetX, int offsetY) {
// オフセットを元に描画範囲を求める
int firstTileX = pixelsToTiles(-offsetX);
int lastTileX = firstTileX + pixelsToTiles(MainPanel.WIDTH) + 1;
// 描画範囲がマップの大きさより大きくならないように調整
lastTileX = Math.min(lastTileX, col);
int firstTileY = pixelsToTiles(-offsetY);
int lastTileY = firstTileY + pixelsToTiles(MainPanel.HEIGHT) + 1;
// 描画範囲がマップの大きさより大きくならないように調整
lastTileY = Math.min(lastTileY, row);
for (int i = firstTileY; i < lastTileY; i++) {
for (int j = firstTileX; j < lastTileX; j++) {
// mapの値に応じて画像を描く
switch (map[i][j]) {
case 'B' : // ブロック
g.drawImage(blockImage, tilesToPixels(j) + offsetX, tilesToPixels(i) + offsetY, null);
break;
}
}
}
}
//(newX, newY)で衝突するブロックの座標を返す スプライトへの参照 newX X座標 newY Y座標 衝突するブロックの座標
public Point getTileCollision(CharObject sprite, double newX, double newY) {
// 小数点以下切り上げ
// 浮動小数点の関係で切り上げしないと衝突してないと判定される場合がある
newX = Math.ceil(newX);
newY = Math.ceil(newY);
double fromX = Math.min(sprite.getX(), newX);
double fromY = Math.min(sprite.getY(), newY);
double toX = Math.max(sprite.getX(), newX);
double toY = Math.max(sprite.getY(), newY);
int fromTileX = pixelsToTiles(fromX);
int fromTileY = pixelsToTiles(fromY);
int toTileX = pixelsToTiles(toX + sprite.getWidth() - 1);
int toTileY = pixelsToTiles(toY + sprite.getHeight() - 1);
// 衝突しているか調べる
for (int x = fromTileX; x <= toTileX; x++) {
for (int y = fromTileY; y <= toTileY; y++) {
// 画面外は衝突
if (x < 0 || x >= col) return new Point(x, y);
if (y < 0 || y >= row) return new Point(x, y);
// ブロックがあったら衝突
if (map[y][x] == 'B') return new Point(x, y);
}
}
return null;
}
//コインブロックがあるか x X座標 y Y座標 コインブロックがあったらtrue
public boolean isCoinBlock(int x, int y) {
if (map[y][x] == 'C') {
return true;
}
return false;
}
//アイテムブロックがあるか x X座標 y Y座標 アイテムブロックがあったらtrue
public boolean isItemBlock(int x, int y) {
if (map[y][x] == 'I') {
return true;
}
return false;
}
//コインブロックを叩かれた状態にする x X座標 y Y座標
public void knockCoinBlock(int x, int y) {
// コインの音を鳴らす
// 叩かれた後のブロックに変化
map[y][x] = 'c';
}
//ピクセル単位をタイル単位に変更する pixels ピクセル単位 タイル単位
public static int pixelsToTiles(double pixels) {
return (int)Math.floor(pixels / TILE_SIZE);
}
//タイル単位をピクセル単位に変更する tiles タイル単位 retun ピクセル単位
public static int tilesToPixels(int tiles) {
return tiles * TILE_SIZE;
}
//イメージをロードする
private void loadImage() {
ImageIcon icon = new ImageIcon(getClass().getResource("/ブロック画像/block.gif"));
blockImage = icon.getImage();
}
//マップをロードする
private void load(String filename) {
try {
// ファイルを開く
BufferedReader br = new BufferedReader(new InputStreamReader(
getClass().getResourceAsStream("/マップデータ/" + filename)));
// 行数を読み込む
String line = br.readLine();
row = Integer.parseInt(line);
// 列数を読み込む
line = br.readLine();
col = Integer.parseInt(line);
// マップを作成
map = new char[row][col];
for (int i = 0; i < row; i++) {
line = br.readLine();
for (int j = 0; j < col; j++) {
map[i][j] = line.charAt(j);
switch (map[i][j]) {
/*
* 敵一覧
* */
case 'b':
sprites.add(new Kuribo(tilesToPixels(j), tilesToPixels(i), "Kuribo.gif", this));
break;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public LinkedList getSprites() {
return sprites;
}
}
---------------------------------------(CharObjectクラス)---------------------------
package Enemy;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import javax.swing.ImageIcon;
import Map.Map;
/*
* キャラクターの共有クラス
* */
public abstract class CharObject {
// 位置
protected double x,
y;
//速度
protected double speed,
jumpSpeed,
vx,
vy;
// 幅 高さ 方向
protected int width,
height,
右,
左,
上,
下,
dir;
protected static int HP,
HpCounter;
// 着地しているか
protected boolean onGround,
forceJump;
protected boolean jumperTwo;
protected boolean canJumperTwo;
protected boolean jumperTree;
protected boolean canJumperTree;
// Char画像
protected Image image;
// アニメーション用カウンタ
protected int count;
// マップへの参照
protected Map map;
public CharObject(double x, double y, String fileName, Map map) {
this.x = x;
this.y = y;
this.map = map;
loadImage(fileName);
count = 0;
}
public abstract void update();
public void draw(Graphics g, int offsetX, int offsetY) {
g.drawImage(image,
(int) x + offsetX, (int) y + offsetY,
(int) x + offsetX + width, (int) y + offsetY + height,
count * width, 0,
count * width + width, height,
null);
}
public boolean isCollision(CharObject sprite) {
Rectangle playerRect = new Rectangle((int)x, (int)y, width, height);
Rectangle spriteRect = new Rectangle((int)sprite.getX(), (int)sprite.getY(), sprite.getWidth(), sprite.getHeight());
if (playerRect.intersects(spriteRect)) {return true;}
return false;
}
private void loadImage(String filename) {
ImageIcon icon = new ImageIcon(getClass().getResource("/キャラクター画像/" + filename));
image = icon.getImage();
}
public class AnimationThread extends Thread {
public void run() {
while (true) {
if (count == 0) {count = 1;}
else if (count == 1) {count = 0;}
try {Thread.sleep(300);}
catch (InterruptedException e) {e.printStackTrace();}
}
}
}
public Point CharPoint(){return new Point((int)x,(int)y);}
public double getX() {return x;}
public double getY() {return y;}
public int getWidth() {return width;}
public int getHeight() {return height;}
}
---------------------------------(Playerクラス)----------------------------
package Player;
import java.awt.Graphics;
import java.awt.Point;
import Enemy.CharObject;
import Map.Map;
public class Player extends CharObject {
private Pshot[] pshot;
private long lastFire = 0;
private static int ShotCount;
private static final int FIRE_INTERVAL = 200;
private static final int NUM_SHOT = 999;
public Player(double x, double y,String filename, Map map) {
super(x, y, filename, map);
this.x = x;
this.y = y;
width = 32;
height = 32;
vx = 0;
vy = 0;
speed = 16.0;
jumpSpeed = 10.0;
onGround = false;
forceJump = false;
jumperTwo = false;
canJumperTwo = true;
jumperTree = false;
canJumperTree = true;
右 = 2;
左 = 1;
dir = 右;
pshot = new Pshot[NUM_SHOT];
for (int i = 0; i < NUM_SHOT; i++) {pshot[i] = new Pshot(0, 0, "PlayerShot.gif", map);}
ShotCount = NUM_SHOT;
AnimationThread thread = new AnimationThread();
thread.start();
}
public void 停止() { vx = 0; }
public void 左へ() {
vx = -speed;
dir = 左;
}
public void 右へ() {
vx = speed;
dir = 右;
}
public void ジャンプ() {
if (onGround || forceJump) {
vy = -jumpSpeed;
onGround = false;
forceJump = false;
} else if (jumperTwo && canJumperTwo) {
vy = -jumpSpeed;
canJumperTwo = false;
} else if (jumperTwo && jumperTree && canJumperTree) {
vy = -jumpSpeed;
canJumperTree = false;
}
}
public void Pショット() {
if (System.currentTimeMillis() - lastFire < FIRE_INTERVAL) {return;}
lastFire = System.currentTimeMillis();
for (int i = 0; i < NUM_SHOT; i++) {
if (pshot[i].isInStorage()) {
Point pos = getPos();
pshot[i].setPos(pos.x + getWidth() / 2, pos.y);
ShotCount--;
break;
}
}
}
public void update() {
vy += Map.GRAVITY;
for (int i = 0; i < NUM_SHOT; i++) {pshot[i].move();}
double newX = x + vx;
Point tile = map.getTileCollision(this, newX, y);
if (tile == null) {
x = newX;
} else {
if (vx > 0) {
x = Map.tilesToPixels(tile.x) - width;
} else if (vx < 0) {
x = Map.tilesToPixels(tile.x + 1);
}
vx = 0;
}
double newY = y + vy;
tile = map.getTileCollision(this, x, newY);
if (tile == null) {
y = newY;
onGround = false;
} else {
if (vy > 0) {
y = Map.tilesToPixels(tile.y) - height;
vy = 0;
onGround = true;
canJumperTwo = true;
canJumperTree = true;
} else if (vy < 0) {
y = Map.tilesToPixels(tile.y + 1);
vy = 0;
if (map.isCoinBlock(tile.x, tile.y)) {
map.knockCoinBlock(tile.x, tile.y);
} else if (map.isCoinBlock(tile.x + 1, tile.y)) {
map.knockCoinBlock(tile.x + 1, tile.y);
}
}
}
}
public void draw(Graphics g, int offsetX, int offsetY) {
g.drawImage(image,
(int) x + offsetX, (int) y + offsetY,
(int) x + offsetX + width, (int) y + offsetY + height,
count * width, dir * height,
count * width + width, dir * height + height,
null);
for (int i = 0; i < NUM_SHOT; i++) {pshot[i].draw(g, offsetX, offsetY);}
}
public Point getPos() { return new Point((int)x, (int)y); }
public int getWidth() { return width; }
public int getHeight() { return height; }
public double getX() { return x; }
public double getY() { return y; }
public double getSpeed() { return speed; }
public void setSpeed(double speed) { this.speed = speed; }
public void setForceJump(boolean forceJump) { this.forceJump = forceJump; }
public void setJumperTwo(boolean jumperTwo) { this.jumperTwo = jumperTwo; }
public void setJumperTree(boolean jumperTree) { this.jumperTree = jumperTree; }
}
---------------------------------------(ShotObjectクラス)-------------------------------------
package Bullet;
import java.awt.Point;
import Enemy.CharObject;
import Map.Map;
public abstract class ShotObject extends CharObject {
// 弾の保管座標(画面に表示されない場所)
protected static final Point STORAGE = new Point(0, 0);
public ShotObject(double x, double y, String filename, Map map) {
super(x, y, filename, map);
x = STORAGE.x;
y = STORAGE.y;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public Point getPos() {
return new Point((int)x,(int) y);
}
public void setPos(int x, int y) {
this.x = x;
this.y = y;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public void store() {
x = STORAGE.x;
y = STORAGE.y;
}
public boolean isInStorage() {
if (x == STORAGE.x && y == STORAGE.x)
return true;
return false;
}
public int Shot左 (){
return dir =左;
}
public int Shot右 (){
return dir =右;
}
}
(PShotクラス -プレイヤー用のショットクラス-)
package Player;
import Bullet.ShotObject;
import Map.Map;
public class Pshot extends ShotObject {
public Pshot(double x, double y, String filename, Map map) {
super(x, y, filename, map);
width = 2;
height = 20;
vx = 0;
vy = 0;
speed = 10;
dir = 右;
}
public void move() {
// 保管庫に入っているなら何もしない
if (isInStorage())
return;
x += speed;
}
@Override
public void update() {
}
}
--------------------------------(Kuribo(敵クラス))---------------------------
package Enemy;
import java.awt.Point;
import Map.Map;
public class Kuribo extends CharObject {
public Kuribo(double x, double y, String filename, Map map) {
super(x, y, filename, map);
width = 32;
height = 32;
speed = 2.5;
vx = -speed;
vy = 0;
HP = 15;
HpCounter = 15;
}
public void update() {
vy += Map.GRAVITY;
double newX = x + vx;
Point tile = map.getTileCollision(this, newX, y);
if (tile == null) {x = newX;}
else {if (vx > 0) {
x = Map.tilesToPixels(tile.x) - width;}
else if (vx < 0) {x = Map.tilesToPixels(tile.x + 1);}
vx = -vx;
}
}
//プレイヤーの位置を返す
public Point getPos() {
return new Point((int)x, (int)y);
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public int 現在のHP() {return HpCounter;}
public int ショットダメージ() {return HpCounter -= 6;}
}
よろしければ、ショットを敵キャラに打たせる方法をお教えください。