package framework.game2D;
import java.math.BigDecimal;
import javax.media.j3d.Appearance;
import javax.media.j3d.Material;
import javax.media.j3d.TextureAttributes;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.TransparencyAttributes;
import javax.vecmath.Vector2d;
import javax.vecmath.Vector3d;
import com.sun.j3d.utils.geometry.Box;
import com.sun.j3d.utils.image.TextureLoader;
import framework.model3D.BaseObject3D;
/**
* 2次元の登場物のクラス
*
* @author T.Kuno
*
*/
public class Sprite implements Movable {
Box box = null;
private TransformGroup transformGroup;
private double collisionRadius;
private float scale = 1.0f;
// 登場物の配置位置
private Position2D position = new Position2D();
// 登場物の配置位置の奥行
private double depth = 0.0;
// 登場物の速度
private Velocity2D velocity = new Velocity2D();
// /////////////////////////////////////////////////
//
// コンストラクタ
//
// /////////////////////////////////////////////////
public Sprite(String imageFile) {
this(imageFile, 1.0f);
}
public Sprite(String imageFile, float scale) {
this(imageFile, scale, 0.0);
}
public Sprite(String imageFile, float scale, double depth) {
this.scale = scale;
transformGroup = new TransformGroup();
Appearance appearance = new Appearance();
if (imageFile != null){
TextureLoader loader = new TextureLoader(imageFile, TextureLoader.BY_REFERENCE, null);
appearance.setTexture(loader.getTexture());
appearance.setCapability(Appearance.ALLOW_TEXTURE_READ);
appearance.setCapability(Appearance.ALLOW_TEXTURE_WRITE);
}
Material material = new Material();
material.setLightingEnable(false);
material.setDiffuseColor(0.0f, 0.0f, 0.0f);
material.setAmbientColor(0.0f, 0.0f, 0.0f);
material.setSpecularColor(0.0f, 0.0f, 0.0f);
material.setEmissiveColor(1.0f, 1.0f, 1.0f);
material.setShininess(1.0f);
appearance.setMaterial(material);
TextureAttributes ta = new TextureAttributes();
ta.setTextureMode(TextureAttributes.REPLACE);
appearance.setTextureAttributes(ta);
TransparencyAttributes transAttributes = new TransparencyAttributes();
transAttributes.setCapability(TransparencyAttributes.ALLOW_BLEND_FUNCTION_READ);
transAttributes.setCapability(TransparencyAttributes.ALLOW_BLEND_FUNCTION_WRITE);
transAttributes.setCapability(TransparencyAttributes.ALLOW_MODE_READ);
transAttributes.setCapability(TransparencyAttributes.ALLOW_MODE_WRITE);
transAttributes.setCapability(TransparencyAttributes.ALLOW_VALUE_READ);
transAttributes.setCapability(TransparencyAttributes.ALLOW_VALUE_WRITE);
transAttributes.setTransparencyMode(TransparencyAttributes.BLENDED);
transAttributes.setTransparency(0.0f);
// transAttributes.setSrcBlendFunction(TransparencyAttributes.BLEND_SRC_ALPHA);
transAttributes.setDstBlendFunction(TransparencyAttributes.BLEND_SRC_ALPHA);
appearance.setTransparencyAttributes(transAttributes);
box = new Box(1.0f * scale, 1.0f * scale, 0.0f, Box.GENERATE_TEXTURE_COORDS | Box.GENERATE_NORMALS, appearance);
transformGroup.addChild(box);
transformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
transformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
position.set(0.0, 0.0);
setPosition(0.0, 0.0, depth);
velocity.set(0.0, 0.0);
setCollisionRadius(1.0);
}
// /////////////////////////////////////////////////
//
// SpriteのPositionに関するメソッド
//
// /////////////////////////////////////////////////
/**
* スプライトの位置を設定する。
*
* @param x
* @param y
*/
public void setPosition(double x, double y) {
position.set(x, y);
Transform3D t3d = new Transform3D();
t3d.setTranslation(new Vector3d(x, y, depth));
transformGroup.setTransform(t3d);
}
/**
* スプライトの位置を設定する。
*
* @param x
* @param y
*/
public void setPosition(double x, double y, double z) {
depth = z;
position.set(x, y);
Transform3D t3d = new Transform3D();
t3d.setTranslation(new Vector3d(x, y, z));
transformGroup.setTransform(t3d);
}
// インターフェースの実装
@Override
public Position2D getPosition() {
return this.position;
}
@Override
public void setPosition(Position2D pos) {
setPosition(pos.getX(), pos.getY());
}
// //////////////////////////////////////////////////////
//
// SpriteのVelocityに関するメソッド
//
// //////////////////////////////////////////////////////
/**
* スプライトの速度を設定する。
*
* @param x
* @param y
*/
public void setVelocity(double x, double y) {
velocity.set(x, y);
}
// インターフェースの実装
@Override
public void setVelocity(Velocity2D vel) {
setVelocity(vel.getX(), vel.getY());
}
@Override
public Velocity2D getVelocity() {
return this.velocity;
}
// /////////////////////////////////////////
//
// Spriteのmotionメソッド
//
// ////////////////////////////////////////
// インターフェースの実装
@Override
public void motion(long interval) {
double frame = (double)interval / 1000.0;
setPosition(position.getX() + frame * velocity.getX(), position.getY() + frame * velocity.getY());
}
/**
* オブジェクトとの衝突判定をし、その結果に応じて物体を動かす
*
* @param interval
* @param mazeGround
* --- 迷路ゲームのステージ
*/
public void motion(long interval, Map2D mazeGround) {
// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// 注意事項及び連絡事項。
// BigDecimalクラスを使って意図的に丸め誤差を行っているのは、座標点や速度ベクトルの成分値などの誤差が原因で
// 衝突判定がうまく機能しない恐れがあったためです。
//
// 原因はわかりませんが、Boxクラスのインスタンスを生成する際にfloat型でBoxの大きさなどを指定し、
// 座標点、速度ベクトルの値がdouble型であり、その値を用いてBoxを動かしているからと推測しています。(単純に、計算処理の結果が間違っている公算大ですが。)
// あるいはカメラの見える範囲を設定(setViewRange(・・・))が原因かと思われます。
//
// 現在も衝突判定が完全に出来ておらずに不具合が入っているため、そのままにしてあります。
//
// 衝突判定の完成度としては、ブロック一つに対しては仕様通りの動きになっています。
// ですが、4個(ブロックがくっついてる状態)の場合だとうまくいかない状態です。
//
// キャッシュ処理もほとんど行っていないため、若干重たく感じると思います。
//
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// スプライトの位置を表す値を小数第2位以降を切り捨て、スプライトの位置を再計算する
position.setX(new BigDecimal(position.getX()).setScale(2,BigDecimal.ROUND_DOWN).doubleValue());
position.setY(new BigDecimal(position.getY()).setScale(2,BigDecimal.ROUND_DOWN).doubleValue());
velocity.setX(new BigDecimal(velocity.getX()).setScale(3,BigDecimal.ROUND_UP).doubleValue());
velocity.setY(new BigDecimal(velocity.getY()).setScale(3,BigDecimal.ROUND_UP).doubleValue());
// 衝突判定する前に一度スプライトの位置を動かす
motion(interval);
// 衝突判定の結果
MapCollisionResult mazeCollisionResult = new MapCollisionResult();
// System.out.println(position.getX() + "," + position.getY());
// System.out.println(velocity.getX() + "," + velocity.getX());
mazeCollisionResult = mazeGround.checkCollision(this);
if (mazeCollisionResult.isCheckColision()) {
velocity.set(mazeCollisionResult.getColisionBackVelocity().getX(), mazeCollisionResult.getColisionBackVelocity().getY());
motion(interval);
mazeCollisionResult.setCheckColision(false);
// ////////////////////////////////////////////////////
//
// 再び衝突判定
//
// ////////////////////////////////////////////////////
// 衝突判定の結果
mazeCollisionResult = mazeGround.checkCollision(this);
if (mazeCollisionResult.isCheckColision()) {
velocity.set(mazeCollisionResult.getColisionBackVelocity().getX(), mazeCollisionResult.getColisionBackVelocity().getY());
motion(interval);
mazeCollisionResult.setCheckColision(false);
}
}
}
// //////////////////////////////
//
// プレイヤーを左方向に動かすメソッド
//
// //////////////////////////////
/**
* キャラクタの位置を左方向に動かす。
*
* @param d
* 動かす量
*/
public void moveLeft(double d) {
this.position.addX(-1.0 * d / 100);
setPosition(position);
}
// //////////////////////////////
//
// プレイヤーを右方向に動かすメソッド
//
// //////////////////////////////
/**
* キャラクタの位置を右方向に動かす。
*
* @param d
* 動かす量
*/
public void moveRight(double d) {
this.position.addX(1.0 * d / 100);
setPosition(position);
}
// //////////////////////////////
//
// プレイヤーを上方向に動かすメソッド
//
// //////////////////////////////
/**
* キャラクタの位置を上方向に動かす。
*
* @param d
* 動かす量
*/
public void moveUp(double d) {
this.position.addY(1.0 * d / 100);
setPosition(position);
}
// //////////////////////////////
//
// プレイヤーを下方向に動かすメソッド
//
// //////////////////////////////
/**
* キャラクタの位置を下方向に動かす。
*
* @param d
* 動かす量
*/
public void moveDown(double d) {
this.position.addY(-1.0 * d / 100);
setPosition(position);
}
// //////////////////////////////
//
// 衝突判定関連のメソッド
//
// //////////////////////////////
/**
* 衝突判定のBounding Sphere(境界球)をcollisionRadiusで設定する
*
* @param collisionRadius
* -- BoundingSphereの半径
*/
public void setCollisionRadius(double collisionRadius) {
this.collisionRadius = collisionRadius;
}
/**
* 衝突判定のBounding Sphere(境界球)の半径を返す
*
* @return BoundingSphereの半径
*/
public double getCollisionRadius() {
return collisionRadius;
}
/**
* イメージを変更する
* @param imageFile イメージファイル名
*/
public void setImage(String imageFile) {
Appearance appearance = box.getAppearance();
if (imageFile != null && appearance != null){
TextureLoader loader = new TextureLoader(imageFile, TextureLoader.BY_REFERENCE, null);
appearance.setTexture(loader.getTexture());
}
}
@Override
public BaseObject3D getBody() {
// TODO Auto-generated method stub
return null;
}
@Override
public TransformGroup getTransformGroupToPlace() {
// TODO Auto-generated method stub
return transformGroup;
}
@Override
public boolean checkCollision(Movable other) {
if (other instanceof Sprite) {
Sprite otherSprite = (Sprite)other;
double thisMinX = getPosition().getX();
double thisMaxX = getPosition().getX() + 2.0 * (double)scale;
double thisMinY = getPosition().getY();
double thisMaxY = getPosition().getY() + 2.0 * (double)scale;
double otherMinX = otherSprite.getPosition().getX();
double otherMaxX = otherSprite.getPosition().getX() + 2.0 * (double)scale;
double otherMinY = otherSprite.getPosition().getY();
double otherMaxY = otherSprite.getPosition().getY() + 2.0 * (double)scale;
if((otherMinX <= thisMinX && otherMaxX >= thisMaxX)
|| (otherMinX >= thisMinX && otherMaxX <= thisMaxX)){
if(otherMaxY > thisMinY && otherMaxY < thisMaxY){
return true;
} else if(otherMinY < thisMaxY && otherMinY > thisMinY){
return true;
}
} else if((otherMinY <= thisMinY && otherMaxY >= thisMaxY)
|| (otherMinY >= thisMinY && otherMaxY <= thisMaxY)){
if(otherMaxX > thisMinX && otherMaxX < thisMaxX){
return true;
} else if(otherMinX < thisMaxX && otherMinX > thisMaxX){
return true;
}
} else if (otherMinX > thisMinX && otherMinX < thisMaxX && otherMaxY > thisMinY && otherMaxY < thisMaxY) {
return true;
} else if (otherMaxX > thisMinX && otherMaxX < thisMaxX && otherMaxY > thisMinY && otherMaxY < thisMaxY) {
return true;
} else if (otherMaxX > thisMinX && otherMaxX < thisMaxX && otherMinY > thisMinY && otherMinY < thisMaxY) {
return true;
} else if (otherMinX > thisMinX && otherMinX < thisMaxX && otherMinY > thisMinY && otherMinY < thisMaxY) {
return true;
}
return false;
} else if (other instanceof Actor2D) {
Vector2d v = new Vector2d();
v.sub(this.getPosition().getVector2d(), other.getPosition()
.getVector2d());
if (v.length() <= (getCollisionRadius() + ((Actor2D)other).getCollisionRadius())) {
return true;
} else {
return false;
}
}
return false;
}
public boolean checkCollisionWithRadius(Movable other) {
if (other instanceof Sprite) {
Sprite otherSprite = (Sprite)other;
double thisMinX = getPosition().getX();
double thisMaxX = getPosition().getX() + 2.0 * collisionRadius;
double thisMinY = getPosition().getY();
double thisMaxY = getPosition().getY() + 2.0 * collisionRadius;
double otherMinX = otherSprite.getPosition().getX();
double otherMaxX = otherSprite.getPosition().getX() + 2.0 * otherSprite.collisionRadius;
double otherMinY = otherSprite.getPosition().getY();
double otherMaxY = otherSprite.getPosition().getY() + 2.0 * otherSprite.collisionRadius;
if((otherMinX <= thisMinX && otherMaxX >= thisMaxX)
|| (otherMinX >= thisMinX && otherMaxX <= thisMaxX)){
if(otherMaxY > thisMinY && otherMaxY < thisMaxY){
return true;
} else if(otherMinY < thisMaxY && otherMinY > thisMinY){
return true;
}
} else if((otherMinY <= thisMinY && otherMaxY >= thisMaxY)
|| (otherMinY >= thisMinY && otherMaxY <= thisMaxY)){
if(otherMaxX > thisMinX && otherMaxX < thisMaxX){
return true;
} else if(otherMinX < thisMaxX && otherMinX > thisMaxX){
return true;
}
} else if (otherMinX > thisMinX && otherMinX < thisMaxX && otherMaxY > thisMinY && otherMaxY < thisMaxY) {
return true;
} else if (otherMaxX > thisMinX && otherMaxX < thisMaxX && otherMaxY > thisMinY && otherMaxY < thisMaxY) {
return true;
} else if (otherMaxX > thisMinX && otherMaxX < thisMaxX && otherMinY > thisMinY && otherMinY < thisMaxY) {
return true;
} else if (otherMinX > thisMinX && otherMinX < thisMaxX && otherMinY > thisMinY && otherMinY < thisMaxY) {
return true;
}
return false;
} else if (other instanceof Actor2D) {
Vector2d v = new Vector2d();
v.sub(this.getPosition().getVector2d(), other.getPosition()
.getVector2d());
if (v.length() <= (getCollisionRadius() + ((Actor2D)other).getCollisionRadius())) {
return true;
} else {
return false;
}
}
return false;
}
}