📄 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");
// lookup table for cos and sin value (0 to 359)
static int[] lookupCosFP = new int[360];
static int[] lookupSinFP = new int[360];
// static init
{
for (int i = 0; i < 360; i++)
lookupCosFP[i] = MathFP.cos(getRadiansFromAngle(i));
for (int i = 0; i < 360; i++)
lookupSinFP[i] = MathFP.sin(getRadiansFromAngle(i));
}
// 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.
public static final int SHIP_START = 0;
public static final int PLAYER_SHIP = SHIP_START + 1;
public static final int ENEMY_SHIP_START = SHIP_START + 2;
public static final int ENEMY_DRONE = SHIP_START + 3;
public static final int ENEMY_AI_START = SHIP_START + 4;
public static final int ENEMY_TURRET = SHIP_START + 5;
public static final int ENEMY_FIGHTER = SHIP_START + 6;
public static final int SHIP_END = SHIP_START + 7;
public static final int BULLET_START = 1000;
public static final int PLASMA_CANNON = BULLET_START + 1;
public static final int BULLET_END = BULLET_START + 2;
private int type; // type of actor (instead of subclassing)
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 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; // maximum spin rate (in degrees per second)
private int maxVelFP; // maximum velocity
private int bounceVelFP; // 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 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
private Actor nextLinked; // link to next actor
private Actor prevLinked; // link to previous actor
public Actor()
{
}
public Actor(World w)
{
world = w;
}
/**
* Initializes an Actor. The alignedDivArg sets the number of distinct angles
* this Actor can face. A Ship with 16 facing images has 16 aligned
* divisions, which translates to an aligned division of 22.5 degrees (360
* divided by 16). The aligned direction is used by the Actor class to make
* sure it can only face an angle that can be drawn.
* @param startX The starting x position of the new Actor.
* @param startY The starting y position of the new Actor.
* @param startDirection The starting direction
* @param alignedDivArg The number of aligned angle divisions.
* @param maxVelFPArg The maximum velocity.
* @param thrustFPArg The starting thrust.
* @param bounceVelFPArg The level of bounce when colliding with something.
* @param maxSpinRateArg The maximum turn rate in degrees per second.
*/
public void init(Actor newOwner, int startX, int startY, int thrustFPArg,
int speedFPArg, int maxVelFPArg, int startDirection,
int alignedDivArg, int bounceVelFPArg, int maxSpinRateArg)
{
setX(startX);
setY(startY);
owner = newOwner;
startingX = x;
startingY = y;
startingDir = startDirection;
maxVelFP = maxVelFPArg;
maxSpinRate = maxSpinRateArg;
wantAlignment = false;
if (alignedDivArg > 0)
{
alignedDivDegreesFP = MathFP.div(360, alignedDivArg);
wantAlignment = true;
}
setDirection(startDirection);
setThrust(thrustFPArg);
setVel(speedFPArg);
bounceVelFP = bounceVelFPArg;
collidable = true;
visible = true;
}
// typically used to restart state for a level (ie. when the player dies)
public void reset()
{
setX(startingX);
setY(startingY);
setDirection(startingDir);
setSpin(0);
setVel(0);
}
public final Actor getNextLinked()
{
return nextLinked;
}
public final void setNextLinked(Actor nextLinked)
{
this.nextLinked = nextLinked;
}
public final Actor getPrevLinked()
{
return prevLinked;
}
public final void setPrevLinked(Actor prevLinked)
{
this.prevLinked = prevLinked;
}
public final int getType()
{
return type;
}
public final void setType(int type)
{
this.type = type;
}
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 boolean isEnemyShip()
{
return (type > ENEMY_SHIP_START && type < SHIP_END);
}
public final boolean isBullet()
{
return (type > BULLET_START && type < BULLET_END);
}
public final Actor getOwner()
{
return owner;
}
public final void setOwner(Actor owner)
{
this.owner = owner;
}
public final void deactivate()
{
collidable = false;
visible = false;
}
/**
* Get a rudamentary distance from this Actor to another (uses basic x, y
* coords rather than the centre to save time - typically you only need an
* approx distance anyway.
*/
public final int distanceTo(Actor another)
{
return distance(x, y, another.getX(), another.getY());
}
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;
}
/**
* Abstract render method that must be implemented by derived classes.
* This method is intended to be called by a Actor manager (such as a
* World class) to draw Actors on the screen.
* @param graphics The graphics context upon which to draw.
* @param offsetX The amount to offset the x drawing position by.
* @param offsetY The amount to offset the y drawing position by.
*/
public void render(Graphics graphics, int offsetX, int offsetY)
{
}
/**
* Abstract method to return the width of the Actor.
*/
abstract public int getWidth();
/**
* Abstract method to return the height of the Actor.
*/
abstract public int getHeight();
/**
* Changes the current speed of the actor by setting the velocity based on
* the current (aligned) direction.
* @param speedArg The speed to travel at.
*/
public final void setVel(int speedArg)
{
xVelFP = MathFP.mul(MathFP.toFP(speedArg), lookupCosFP[alignedDir]);
yVelFP = MathFP.mul(MathFP.toFP(speedArg), -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;
}
/**
* Gets the closest aligned direction to the passed in direction (in
* degrees).
* @param dir The direction to align (in degrees).
* @return A direction which aligns to the number of divisions the Actor
* supports.
*/
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;
}
/**
* @return The current aligned direction.
*/
public final int getDirection()
{
return alignedDir;
}
/**
* @return The current real direction (not aligned).
*/
public final int getRealDirection()
{
return realDir;
}
/**
* @return The World object that contains this Actor.
*/
public final World getWorld()
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -