package framework.gameMain;
import java.util.ArrayList;
import framework.animation.Animation3D;
import framework.model3D.CollisionResult;
import framework.model3D.GeometryUtility;
import framework.model3D.Movable;
import framework.model3D.Object3D;
import framework.model3D.Position3D;
import framework.model3D.Quaternion3D;
import framework.physics.Force3D;
import framework.physics.Ground;
import framework.physics.PhysicsUtility;
import framework.physics.Solid3D;
import framework.physics.Velocity3D;
import java3d.AxisAngle4d;
import java3d.Transform3D;
import java3d.Vector3d;
/**
* ゲーム内の登場物全般
* @author 新田直也
*
*/
public abstract class Actor extends Animatable implements Movable {
protected Vector3d direction = new Vector3d(1.0, 0.0, 0.0);
protected Mode mode;
// 以下省メモリ化のため予めインスタンスを生成
protected Mode modeFreeFall = new ModeFreeFall();
protected Mode modeOnGround = new ModeOnGround();
public Actor(Object3D body, Animation3D animation) {
super(new Solid3D(body), animation);
mode = modeOnGround;
}
public Actor(Solid3D body, Animation3D animation) {
super(body, animation);
mode = modeOnGround;
}
// public Actor(ActorModel model) {
// super(model.createBody(), model.getAnimation());
// mode = modeOnGround;
// }
/**
* 単位時間ごとの動作(衝突判定処理は行わない)
* @param interval --- 前回呼び出されたときからの経過時間(ミリ秒単位)
*/
public void motion(long interval){
// 1. 位置を動かす
((Solid3D)body).move(interval, getGravity(), getGravityCenter());
super.motion(interval);
}
/**
* 単位時間ごとの動作(衝突判定処理も行う)
* @param interval --- 前回呼び出されたときからの経過時間(ミリ秒単位)
* @param ground --- 地面(構造物)
*/
public void motion(long interval, Ground ground) {
// 1. 位置を動かす
((Solid3D)body).move(interval, getGravity(), getGravityCenter());
if (animation != null) {
// 2. アニメーションを実行
if (animation.progress(interval) == false) {
onEndAnimation();
}
// 3. 姿勢を変える
body.apply(animation.getPose(), false);
}
// 4. 衝突判定
CollisionResult cr = PhysicsUtility.doesIntersect((Solid3D)body, ground);
// 5. 衝突応答
if (cr != null) {
// 構造物にぶつかった、または接触している時
if (cr.normal.dot(PhysicsUtility.vertical) > GeometryUtility.TOLERANCE) {
// 上向きの面(=地面)にぶつかった、または接触している時
if (cr.length <= GeometryUtility.TOLERANCE) {
// 地面に乗っている
if (!(mode instanceof ModeOnGround)) {
// 落ちてきて丁度乗った
mode = modeOnGround;
((ModeOnGround)mode).setNormal(cr.normal);
onEndFall();
}
} else {
// 地面にめり込んだ
// 5.1. 押し戻す
onIntersect(cr, interval);
if (!(mode instanceof ModeOnGround)) {
// 落ちてきてめり込んだ
// 6. 地面モードにする
mode = modeOnGround;
((ModeOnGround)mode).setNormal(cr.normal);
onEndFall();
} else {
// 歩いている途中で、アニメーションの関係で一瞬だけめり込んだ
// または、歩いている途中で斜面に差し掛かった
((ModeOnGround)mode).setNormal(cr.normal);
}
}
} else if (cr.normal.dot(PhysicsUtility.vertical) >= -GeometryUtility.TOLERANCE) {
// 垂直壁にめり込んだ
// 5.1. 押し戻す
onIntersect(cr, interval);
} else {
// 下からぶつかった、または接触した(頭をぶつけた)
if (cr.length > GeometryUtility.TOLERANCE) {
// 5.1. 押し戻す
onIntersect(cr, interval);
}
}
cr = null;
} else {
// 地面とぶつかっても接触してもいない時
// 6. 落下モードにする
mode = modeFreeFall;
}
}
public void motion(long interval, Ground ground,ArrayList<Force3D> forces,
ArrayList<Position3D> appPoints) {
forces.add(getGravity());
appPoints.add(getGravityCenter());
// 1. 位置を動かす
((Solid3D)body).move(interval, forces, appPoints);
if (animation != null) {
// 2. アニメーションを実行
if (animation.progress(interval) == false) {
onEndAnimation();
}
// 3. 姿勢を変える
body.apply(animation.getPose(), false);
}
// 4. 衝突判定
CollisionResult cr = PhysicsUtility.doesIntersect((Solid3D)body, ground);
// 5. 衝突応答
if (cr != null) {
// 構造物にぶつかった、または接触している時
if (cr.normal.dot(PhysicsUtility.vertical) > GeometryUtility.TOLERANCE) {
// 上向きの面(=地面)にぶつかった、または接触している時
if (cr.length <= GeometryUtility.TOLERANCE) {
// 地面に乗っている
if (!(mode instanceof ModeOnGround)) {
// 落ちてきて丁度乗った
mode = modeOnGround;
((ModeOnGround)mode).setNormal(cr.normal);
onEndFall();
}
} else {
// 地面にめり込んだ
// 5.1. 押し戻す
onIntersect(cr, interval);
if (!(mode instanceof ModeOnGround)) {
// 落ちてきてめり込んだ
// 6. 地面モードにする
mode = modeOnGround;
((ModeOnGround)mode).setNormal(cr.normal);
onEndFall();
} else {
// 歩いている途中で、アニメーションの関係で一瞬だけめり込んだ
// または、歩いている途中で斜面に差し掛かった
((ModeOnGround)mode).setNormal(cr.normal);
}
}
} else if (cr.normal.dot(PhysicsUtility.vertical) >= -GeometryUtility.TOLERANCE) {
// 垂直壁にめり込んだ
// 5.1. 押し戻す
onIntersect(cr, interval);
} else {
// 下からぶつかった、または接触した(頭をぶつけた)
if (cr.length > GeometryUtility.TOLERANCE) {
// 5.1. 押し戻す
onIntersect(cr, interval);
}
}
cr = null;
} else {
// 地面とぶつかっても接触してもいない時
// 6. 落下モードにする
mode = modeFreeFall;
}
}
public void setInitialDirection(Vector3d dir) {
direction = dir;
}
public Vector3d getInitialDirection() {
return direction;
}
/**
* 指定した方向に向かせる
* @param vec 新しい向き
*/
public void setDirection(Vector3d vec) {
Vector3d v1 = new Vector3d();
Vector3d v2 = new Vector3d();
v1.cross(direction, GeometryUtility.Y_AXIS);
v2.cross(vec, GeometryUtility.Y_AXIS);
if (v2.length() < GeometryUtility.TOLERANCE) return;
v1.normalize();
v2.normalize();
double cos = v1.dot(v2);
v1.cross(v1, v2);
double sin = v1.dot(GeometryUtility.Y_AXIS);
double angle = Math.atan2(sin, cos);
AxisAngle4d axisAngle = new AxisAngle4d(GeometryUtility.Y_AXIS, angle);
Quaternion3D quat = new Quaternion3D(axisAngle);
((Solid3D)body).apply(quat, false);
}
/**
* 指定した方向に向かせる
* @param vec 新しい向き
*/
public void setDirection3D(Vector3d vec) {
Vector3d v1 = new Vector3d();
Vector3d v2 = new Vector3d();
v1.cross(direction, GeometryUtility.Y_AXIS);
double angle = Math.PI / 2.0 - Math.acos(vec.dot(GeometryUtility.Y_AXIS));
AxisAngle4d axisAngle2 = new AxisAngle4d(v1, angle);
v2.cross(vec, GeometryUtility.Y_AXIS);
if (v2.length() < GeometryUtility.TOLERANCE) return;
v1.normalize();
v2.normalize();
double cos = v1.dot(v2);
v1.cross(v1, v2);
double sin = v1.dot(GeometryUtility.Y_AXIS);
angle = Math.atan2(sin, cos);
AxisAngle4d axisAngle = new AxisAngle4d(GeometryUtility.Y_AXIS, angle);
Quaternion3D quat = new Quaternion3D(axisAngle);
Quaternion3D quat2 = new Quaternion3D(axisAngle2);
quat.mul(quat2);
((Solid3D)body).apply(quat, false);
}
/**
* 現在向いている方向を取得する
* @return 現在の向き
*/
public Vector3d getDirection() {
Vector3d dir = new Vector3d(direction);
Transform3D trans = new Transform3D();
trans.set(((Solid3D)body).getQuaternion().getAxisAngle());
trans.transform(dir);
return dir;
}
/**
* 移動速度ベクトルを設定する
* @param vel 新しい移動速度ベクトル
*/
public void setVelocity(Velocity3D vel) {
((Solid3D)body).apply(vel, false);
}
/**
* 移動速度ベクトルを取得する
* @return 現在の移動速度ベクトル
*/
public Velocity3D getVelocity() {
return ((Solid3D)body).getVelocity();
}
/**
* X軸を中心に回転する
* @param angle 回転角(反時計回り, 単位:ラジアン)
*/
public void rotX(double angle) {
Quaternion3D curQuat = body.getQuaternion();
curQuat.add(new AxisAngle4d(GeometryUtility.X_AXIS, angle));
body.apply(curQuat, false);
}
/**
* Y軸を中心に回転する
* @param angle 回転角(反時計回り, 単位:ラジアン)
*/
public void rotY(double angle) {
Quaternion3D curQuat = body.getQuaternion();
curQuat.add(new AxisAngle4d(GeometryUtility.Y_AXIS, angle));
body.apply(curQuat, false);
}
/**
* Z軸を中心に回転する
* @param angle 回転角(反時計回り, 単位:ラジアン)
*/
public void rotZ(double angle) {
Quaternion3D curQuat = body.getQuaternion();
curQuat.add(new AxisAngle4d(GeometryUtility.Z_AXIS, angle));
body.apply(curQuat, false);
}
/**
* 加わっている重力を取得する
* @return 重力
*/
public Force3D getGravity() {
return mode.getForce((Solid3D)body);
}
/**
* 重心を取得する
* @return 重心位置
*/
public Position3D getGravityCenter() {
return ((Solid3D)body).getGravityCenter();
}
/**
* 地面に乗っている状態か否かを取得する
* @return true --- 地面に乗っている, false --- 地面に乗っていない(空中にいる)
*/
public boolean isOnGround() {
return (mode instanceof ModeOnGround);
}
/**
* 地面(構造物)に落下した瞬間に呼び出される
*/
public abstract void onEndFall();
/**
* 地面(構造物)に衝突した瞬間に呼び出される
* @param normal --- 地面の法線方向ベクトル
* @param interval --- 前回の動作からの経過時間(ミリ秒単位)
*/
public abstract void onIntersect(CollisionResult normal, long interval);
}