📄 actor.java
字号:
/**
* A dynamic moving object capable of existing in a World.
*/
import javax.microedition.lcdui.Graphics;
import net.jscience.math.kvm.MathFP;
abstract public class Actor
{
public static final int FP_MINUS_1 = MathFP.toFP("-1.0");
public static final int FP_MINUS_05 = MathFP.toFP("-0.5");
public static final int FP_MINUS_01 = MathFP.toFP("-0.1");
public static final int FP_225 = MathFP.toFP("22.5");
public static final int FP_PI2 = MathFP.mul(MathFP.PI, MathFP.toFP(2));
public static final int FP_DEGREES_PER_RAD = MathFP.div(MathFP.toFP(360), FP_PI2);
public static final int FP_ONE = MathFP.toFP("1");
// Actor types - we place them all in here so we can check the type of
// a actor instance without doing an expensive instanceof test. This is
// (of course) a horrible corruption of the intent of an abstract Actor
// class - it's considered worth it for the performance gain.
private int xFP, yFP; // current position
private int x, y; // integer versions (stored for speed)
private int startingX, startingY; // where the actor starts (used for restart)
private int startingDir; // starting direction (used for restart)
private int lastXFP, lastYFP;
private int lastX, lastY;
private int xVelFP, yVelFP; // current velocity
private int xAccFP, yAccFP; // current acceleration
private int thrustFP;
private int spinFP; // current spin rate (in degrees per second)
private int maxSpinRate; // current spin rate (in degrees per second)
private int maxVelFP; // maximum velocity
private int bounceVel; // velocity to variance when bouncing off an impact
private int realDir; // actual direction in degrees
private int alignedDir; // aligned direction
private int alignedDivDegreesFP; // degrees per facing division
private boolean wantAlignment; // turn auto alignment on
private Actor nextInZMap; // used by zmap to ref next actor
private Actor prevInZMap; // used by zmap to ref prev actor
private long fluff = 0;
private boolean autoSpinning; // we set a flag to avoid over calling setSpin()
private int targetAngle;
protected Actor owner;
protected World world;
private boolean collidable; // can anything collide with us?
private boolean visible; // whether we should be drawn or not
public Actor()
{
}
public Actor(World w)
{
world = w;
}
public void init(Actor newOwner, int xArg, int yArg, int thrustArg, int speedArg, int maxVelArg,
int dirArg, int alignedDivArg, int bounceVelArg, int maxSpinRateArg)
{
setX(xArg);
setY(yArg);
owner = newOwner;
startingX = x;
startingY = y;
startingDir = dirArg;
maxVelFP = MathFP.toFP(maxVelArg);
maxSpinRate = maxSpinRateArg;
wantAlignment = false;
if (alignedDivArg > 0)
{
alignedDivDegreesFP = MathFP.div(360, alignedDivArg);
wantAlignment = true;
}
setDirection(dirArg);
setThrust(thrustArg);
setVel(speedArg);
bounceVel = bounceVelArg;
collidable = true;
visible = true;
}
public void setNextInZMap(Actor a) { nextInZMap = a; }
public Actor getNextInZMap() { return nextInZMap; }
public void setPrevInZMap(Actor a) { prevInZMap = a; }
public Actor getPrevInZMap() { return prevInZMap; }
// typically used to restart state for a level (ie. when the player dies)
public void reset()
{
int oldX = x;
int oldY = y;
setX(startingX);
setY(startingY);
setDirection(startingDir);
setSpin(0);
}
public int getStartingY()
{
return startingY;
}
public int getStartingX()
{
return startingX;
}
public final void setStartingPos(int startingX, int startingY)
{
this.startingX = startingX;
this.startingY = startingY;
}
public final Actor getOwner()
{
return owner;
}
public final void setCollidable(boolean b)
{
collidable = b;
}
public final boolean isCollidable()
{
return collidable;
}
public final boolean isVisible()
{
return visible;
}
public final void setVisible(boolean visible)
{
this.visible = visible;
}
public void render(Graphics g, int offsetX, int offsetY)
{
}
abstract public int getWidth();
abstract public int getHeight();
public final void setVel(int speedArg)
{
xVelFP = MathFP.mul(MathFP.toFP(speedArg), World.lookupCosFP[alignedDir]);
yVelFP = MathFP.mul(MathFP.toFP(speedArg), -World.lookupSinFP[alignedDir]);
// If you change this remember to change the cycle code
if (xVelFP > maxVelFP)
xVelFP = maxVelFP;
else if (xVelFP < -maxVelFP) xVelFP = -maxVelFP;
if (yVelFP > maxVelFP)
yVelFP = maxVelFP;
else if (yVelFP < -maxVelFP) yVelFP = -maxVelFP;
}
/**
* Set the direction (in degrees; east = 0). We also set the alignedDir
* to the closest angle available.
*/
public final void setDirection(int d)
{
realDir = d;
if (realDir < 0) realDir = (359 + (realDir)); // add the neg value
if (realDir > 359) realDir = (d - 360);
// set the facing direction to be the closest alignment
if (wantAlignment)
{
alignedDir = getAlignedDirection(realDir);
}
else
alignedDir = realDir;
}
public final int getAlignedDirection(int dir)
{
int fp = MathFP.toInt(MathFP.div(MathFP.toFP(dir), alignedDivDegreesFP));
int ad = MathFP.toInt(MathFP.mul(MathFP.toFP(fp), alignedDivDegreesFP));
if (ad < 0) ad = 0;
if (ad > 359) ad = 0;
return ad;
}
public final int getDirection()
{
return alignedDir;
}
public final int getRealDirection()
{
return realDir;
}
public final World getWorld()
{
return world;
}
/**
* set spin rate in degrees per second
* @param i 0-360 degrees
*/
public final void setSpin(int i)
{
spinFP = MathFP.toFP(i);
}
public final void setTargetDirection(int angle)
{
// set an angle this actors wants to face; the actor will start spinning
// at its default spin rate towards the target angle - see cycle for
// the actual spin code
targetAngle = angle;
autoSpinning = false;
}
public final void setThrust(int i)
{
thrustFP = MathFP.div(MathFP.toFP(i), MathFP.toFP(3));
}
public final int getThrust()
{
return MathFP.toInt(thrustFP);
}
public final boolean isThrusting()
{
return MathFP.toInt(thrustFP) != 0;
}
public final int getX()
{
return x;
}
public final int getY()
{
return y;
}
public final void setX(int xArg)
{
xFP = MathFP.toFP(xArg);
x = xArg;
}
public final void setY(int yArg)
{
yFP = MathFP.toFP(yArg);
y = yArg;
}
public final int getCenterX()
{
return x + (getWidth() / 2);
}
public final int getCenterY()
{
return getY() + (getHeight() / 2);
}
public final boolean isCollidingWith(int px, int py)
{
if (px >= x && px <= (x + getWidth()) &&
py >= y && py <= (y + getHeight()))
return true;
return false;
}
public final boolean isCollidingWith(int ax, int ay, int w, int h)
{
if (y + getHeight() < ay || y > ay + h ||
x + getWidth() < ax || x > ax + w)
return false;
return true;
}
public final boolean isCollidingWith(Actor another)
{
return isCollidingWith(another.getX(), another.getY(), another.getWidth(), another.getHeight());
}
public void suicide()
{
}
public void cycle(long deltaMS)
{
int ticks = (int) (deltaMS + fluff) / 100;
// remember the bit we missed
fluff += (deltaMS - (ticks * 100));
if (ticks > 0)
{
int ticksFP = MathFP.toFP(ticks);
// move towards our target direction, if we have one
if (targetAngle != 0)
{
if (!autoSpinning)
{
// start spin in the dir of the target angle
setSpin(isClockwise(getDirection(), targetAngle) ? -maxSpinRate : maxSpinRate);
}
// and check if we've made it to the target direction
if (getAlignedDirection(targetAngle) == getDirection())
{
setSpin(0);
setTargetDirection(0);
autoSpinning = false;
}
}
// spin based on degrees per tick
if (spinFP != 0)
setDirection(getRealDirection() + MathFP.toInt(MathFP.mul(ticksFP, spinFP)));
// figure out the amount of movement based on our speed in pixels per ticks
if (thrustFP != 0)
{
//xAccFP = MathFP.mul(thrustFP, lookupCosFP[alignedDir]);
//yAccFP = MathFP.mul(thrustFP, lookupSinFP[alignedDir]);
//xVelFP = MathFP.add(xVelFP, xAccFP);
//yVelFP = MathFP.add(yVelFP, yAccFP);
xVelFP = MathFP.mul(thrustFP, World.lookupCosFP[alignedDir]);
yVelFP = MathFP.mul(thrustFP, -World.lookupSinFP[alignedDir]);
}
// If you change this remember to change the setVel code
if (xVelFP > maxVelFP)
xVelFP = maxVelFP;
else if (xVelFP < -maxVelFP) xVelFP = -maxVelFP;
if (yVelFP > maxVelFP)
yVelFP = maxVelFP;
else if (yVelFP < -maxVelFP) yVelFP = -maxVelFP;
lastXFP = xFP;
lastX = x;
lastYFP = yFP;
lastY = y;
// adjust X
xFP = MathFP.add(xFP, MathFP.mul(xVelFP, ticksFP));
x = MathFP.toInt(xFP);
// now check if we collided with anything (we test X first)
if (collidable)
{
// collisions are disabled in the raycaster
}
// adjust Y
// we also handle a special case where the x collision may have
// caused the ship to move (teleport, gateway). In this case our
// lastYFP is invalid and we should abort the collision test.
if (lastYFP == yFP)
{
yFP = MathFP.add(yFP, MathFP.mul(yVelFP, ticksFP));
y = MathFP.toInt(yFP);
// now check if we collided with anything in Y movement
if (collidable)
{
// collisions are disabled in the raycaster
}
}
}
}
public void onCollision(Actor another)
{
}
/******************************* STATICS **********************************/
/**
* returns the shortest turning direction from one angle to another
*/
public final static boolean isClockwise(int angleA, int angleB)
{
if (angleA > angleB)
return (Math.abs(angleA - angleB)) < (angleB + (360 - angleA));
else
return (angleA + (360 - angleB)) < (Math.abs(angleB - angleA));
}
public final static int getFacingAngle(int x1, int y1, int x2, int y2)
{
// figure the two sides of our right angle triangle
int a = MathFP.toFP(Math.abs(x2 - x1));
int b = MathFP.toFP(Math.abs(y2 - y1));
if (a == 0) a = FP_ONE;
if (b == 0) b = FP_ONE;
int bovera = MathFP.div(b, a);
int angleInRadians = MathFP.atan(bovera);
int angle = getAngleFromRadians(angleInRadians);
// now adjust for which quadrant we're really in
if (x2 < x1)
{
// left side
if (y2 < y1)
return angle + 180;
return angle + 90;
}
else
{
// right side
if (y2 < y1)
return angle + 180;
return angle;
}
}
public final static int getAngleFromRadians(int radiansFP)
{
return MathFP.toInt(MathFP.mul(radiansFP, FP_DEGREES_PER_RAD));
}
public final static int getRadiansFPFromAngle(int angle)
{
return MathFP.div(MathFP.toFP(angle), FP_DEGREES_PER_RAD);
}
/**
* returns the opposite of an angle (ie. 0=180, 90=270)
* @param angle
*/
public final static int getOppositeAngle(int angle)
{
if (angle < 180) return angle + 180;
return angle - 180;
}
/**
* Project a point along a vector
* @param x starting x position
* @param y starting y position
* @param angle angle to project along
* @param distance distance to project
* @return int[] containing x and y int positions
*/
public final static int[] getProjectedPos(int x, int y, int angle, int distance)
{
int dx = World.lookupCosFP[angle];
int dy = World.lookupSinFP[angle];
int xFP = MathFP.toFP(x);
int yFP = MathFP.toFP(y);
int distanceFP = MathFP.toFP(distance);
xFP = MathFP.add(xFP, MathFP.mul(dx, distanceFP));
yFP = MathFP.add(yFP, MathFP.mul(dy, distanceFP));
//System.out.println("origX=" + x + " y=" + y + " dx=" + MathFP.toString(dx) + " dy=" + MathFP.toString(dy) + " " +
// "distance=" + MathFP.toString(distanceFP) +
// " x=" + MathFP.toString(xFP) + " y=" + MathFP.toString(yFP) + " rad=" +
// MathFP.toString(radAngleFP));
int[] result = {MathFP.toInt(xFP), MathFP.toInt(yFP)};
return result;
}
public final static int distance(int x1, int y1, int x2, int y2)
{
int dx = (x2 - x1) * (x2 - x1);
int dy = (y2 - y1) * (y2 - y1);
if (dx == 0 || dy == 0) return 0;
try
{
return MathFP.toInt(MathFP.sqrt(MathFP.toFP(dx + dy)));
}
catch (ArithmeticException ae)
{
return 0;
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -