📄 enemy.java
字号:
import javax.microedition.lcdui.Graphics;
public class Enemy {
/**
* 构建一个敌人需要初始化的基本数据
* @param GE GameEngine游戏主轴
* @param enemyData short[] 敌人的初始化数据
*/
public Enemy(Engine GE, short[] enemyData)
{
engine = GE;
objectIndex = (byte) enemyData[0]; //此敌人在动画数组中的索引
x = enemyData[1]; //初始化敌人的位置(相对地图的x坐标)
y = (short) (enemyData[2]); //初始化敌人的位置(相对地图的y坐标)
startX = x; //记录敌人的初始位置
startY = y;
bround = enemyData[3]; //敌人的巡逻范围
xSpeed = (byte) enemyData[4]; //敌人x方向的移动速度
ySpeed = (byte) enemyData[5]; //敌人y方向的移动速度
isLeft = enemyData[6] == 0; //敌人的初始化方向
setStatus(STATE_RUN); //敌人的初始化状态
isDead = false; //初始敌人设置为未死亡状态
upStep = 0; //每帧向上移动的像素为0(即y方向无位移)
HP = 100; //敌人的初始生命值
getSize();
}
final static byte STATE_STAND = 0; //敌人的站立状态
final static byte STATE_RUN = 1; //行走状态
final static byte STATE_CHASE = 2; //追击主角状态
final static byte STATE_ATTACK = 3; //攻击
final static byte STATE_INJURE = 4; //受伤
final static byte STATE_DEAD = 5; //死亡
final static byte STATE_END = 6; //怪物结束
protected Engine engine;
protected short HP; //敌人的生命
protected short x; //敌人的位置(相对地图的x坐标)
protected short y; //敌人的位置(相对地图的y坐标)
protected short w; //敌人的宽度(用来矩形碰撞处理)
protected short h; //敌人的宽度
protected short bround; //敌人的巡逻范围
protected boolean isLeft; //敌人的方向
protected byte upStep; //0表示y方向没有移动,> 0向下移动 ,< 0向上移动
protected byte xSpeed; //敌人的x方向的移动速度
protected byte ySpeed; //敌人的y方向的移动速度
protected boolean isDead; //敌人是否死亡(是否存在)
protected byte curStatus; //敌人的状态标记量
protected short startX; //记录敌人的初始位置
protected short startY;
protected short px; //敌人的可碰撞区域(px,py,pw,ph构成的矩形)的起始X方向的坐标
protected short py; //敌人的可碰撞区域(px,py,pw,ph构成的矩形)的起始Y方向的坐标
protected short pw; //敌人的可碰撞区域(px,py,pw,ph构成的矩形)的宽度
protected short ph; //敌人的可碰撞区域(px,py,pw,ph构成的矩形)的高度
protected byte objectIndex; //此敌人在动画数组中的索引
private byte actionIndex; //敌人的动作(站立、移动、攻击、追击的一种)
private byte frameIndex; //敌人的帧
private byte aniIndex; //切换帧变化的中间变量
/**
* 敌人的逻辑
* 包括敌人的AI实现、敌人的碰撞及多种攻击方式
*/
protected final void move()
{
switch (curStatus)
{
case STATE_STAND:
playAni(STATE_RUN,0);
break;
case STATE_RUN:
playAni(-1,0);
// actionIndex = 1;
if(x > startX +bround||
x < startX - bround){
isLeft = x > startX;
}
/*这里注意区分isDead 与主角死亡状态的区别:isDead = true 表示主角已经从屏幕内显示。
主角处于死亡状态则表示主角已经死亡,但是还是存在的。显示部分需要显示主角死亡的动画。
此状态下敌人的攻击、追击逻辑不执行。但是主角逻辑依然执行*/
if(!engine.role.isDead&&//主角没有死亡
engine.role.curStatus != Role.STATE_DEAD&&//且主角不是死亡状态
engine.role.curStatus != Role.STATE_END&&//且主角不是结束状态
engine.role.x < startX + bround&&//主角在敌人的视野内,敌人开始追击
engine.role.x > startX - bround&&
Math.abs(engine.role.y - y) < h){
setStatus(STATE_CHASE);
}
else{
enemyMoveStep(4);
}
break;
case STATE_CHASE:
playAni(-1,0);
isLeft = x > engine.role.x;//追击状态下 敌人面向主角
if(engine.role.x > startX +2*bround||
engine.role.x < startX - 2*bround){//超出追击范围 返回巡逻状态(行走状态),预设定主角的追击范围为巡逻范围的2倍(可随意设定)
setStatus(STATE_RUN);
}
else if(isLeft == (x > engine.role.x )&&//方向面向主角
Math.abs(engine.role.x - x ) <= engine.role.w /2 + w/2){//且敌人与主角的位置间隔小于0时(这里主角的x是主角中心点的x)
setStatus(STATE_ATTACK);
}
else{
enemyMoveStep(6);
}
break;
case STATE_ATTACK:
if(engine.role.curStatus == Role.STATE_INJURE||
engine.role.curStatus == Role.STATE_DEAD||
engine.role.curStatus == Role.STATE_END||
engine.role.isDead){
setStatus(STATE_STAND);
}
else
if(frameIndex == 3){//这里选取此怪物的攻击帧为第三帧
getAttackSize(2);//2表示攻击状态对应的动画数据为2。也就时动画数据中第二维对应输入的参数
if (Tools.hit(px, py, pw, ph, engine.role.x-(engine.role.w>>1), engine.role.y-engine.role.h, engine.role.w, engine.role.h))
{
engine.role.setStatus(Role.STATE_INJURE);
}
}
playAni(STATE_CHASE,1);
break;
case STATE_INJURE:
playAni(STATE_CHASE,1);
break;
case STATE_DEAD:
playAni(STATE_END,1);
break;
case STATE_END:
isDead = true;
break;
}
}
/**
* 根据敌人的类型、动作、帧来显示此敌人的动画
* @param g Graphics
*/
protected final void drawEnemy(Graphics g)
{
if(curStatus < actionArray.length)
actionIndex = actionArray[curStatus];
Tools.drawNpcItemData(g, objectIndex, actionIndex, frameIndex, x, y, isLeft);
}
/**
* 切换敌人的状态,目标状态的动作索引
* @param nextState int 目标状态(需要切换至此状态)
* @param nextaActionIndex int 目标状态的动作索引
*/
protected final void setStatus(int nextState)
{
if (curStatus == STATE_DEAD)
{ //已经死亡状态只能切换为结束状态
if(nextState != STATE_END)
return;
}
switch (nextState)
{
case STATE_INJURE:
HP -= 5;
if (HP <= 0)
{
curStatus = STATE_DEAD;
aniIndex = 0; //改变状态后需要初始化下一状态的帧索引
frameIndex = 0;
return;
}
break;
}
aniIndex = 0; //改变状态后需要初始化下一状态的帧索引
frameIndex = 0;
curStatus = (byte) nextState;
}
/**
* 获得攻击帧的攻击区域
* @param tempActionIndex int
*/
protected final void getAttackSize(int tempActionIndex)
{
int tempFrameIndex = 0;
//根据游戏类型获得游戏的攻击帧
switch (objectIndex)
{
case 1: //索引为1的怪物的攻击帧
tempFrameIndex = 3;//怪物的攻击帧为第三帧
break;
default: //其他怪物的攻击帧
tempFrameIndex = 0; //默认为第0帧
break;
}
//以下部分获得攻击帧
try
{
/*根据动画数据我们定义数据npcItemDataSize 来保存所有动画数据中的每帧的宽、高以及X、Y方向最小偏移,存贮格式为:
第一维:表示对象的索引(即objectIndex表示某个对象,与动画数据的对象索引对应,所以此维的长度与npcItemData长度相同)。
第二维:表示此对象的动作的索引。
第三维:所有帧的x方向的最大偏移,Y方向的最大偏移,宽度、高度。如果是很多帧则下4为分别为第二帧的x方向的最大偏移,Y方向的最大偏移,宽度、高度。依此类推。
*/
pw = Data.npcItemDataSize[objectIndex][tempActionIndex][tempFrameIndex * 4 + 2];
//从存贮动画数据的每帧的宽、高以及X、Y方向最小偏移的数组中获得,数据npcItemDataSize的或者后面讲述。
ph = Data.npcItemDataSize[objectIndex][tempActionIndex][tempFrameIndex * 4 + 3];
px = (short) (x +
Data.npcItemDataSize[objectIndex][tempActionIndex][tempFrameIndex * 4 + 0]);
py = (short) (y +
Data.npcItemDataSize[objectIndex][tempActionIndex][tempFrameIndex * 4 + 1]);
}
catch (Exception ex)
{
}
}
/**
* 获得敌人当前帧的宽度、高度
*与getAttackSize(int tempActionIndex)区别在于,此方法为此敌人通常情况下的宽度、高度,而方法getAttackSize(int tempActionIndex)获得的则是攻击时才使用到的攻击碰撞区域
*/
protected final void getSize()
{
w = Data.npcItemDataSize[objectIndex][0][0 * 4 + 2]; //从保存每帧动画的尺寸的数组中获得当前帧的宽度、高度
h = Data.npcItemDataSize[objectIndex][0][0 * 4 + 3];
}
/**
* nextState > 0 切换状态
* nextState = -1 循环播放
* nextState < -1 停留在最后一帧
* @param nextState byte
* 同主角类里的改变帧的部分
*/
private final void playAni(int nextState, int nextaActionIndex)
{
/*当达到最后一帧时*/
if (aniIndex >= Data.frameItemIndex[objectIndex][actionIndex].length)
{
if (nextState >= 0)
{
aniIndex = 0; //初始化下状态下的中间索引
frameIndex = Data.frameItemIndex[objectIndex][actionIndex][aniIndex]; //初始化下状态下的帧索引
setStatus(nextState); //切换至目标状态
return;
}
else if (nextState == -1)
{ //当动画帧显示到此状态下的最后一帧时,输入参数为-1则表示需要无限循环播放此状态下的动画。初始化为第一帧。
aniIndex = 0;
}
else
{ //否则为显示最后一帧直至切换状态为止
return;
}
}
frameIndex = Data.frameItemIndex[objectIndex][actionIndex][aniIndex]; //获得现在需要显示的实际帧
aniIndex++;
}
/**
* 敌人的每步移动
* @param step int
*/
void enemyMoveStep(int step){
if(isLeft){
if(Map.isFall(x - step,y) ||
!Map.isRun(x - step,y-(h>>1))){
isLeft = !isLeft;
setStatus(STATE_RUN);
}
else
x -= step;
}
else
{
if (Map.isFall(x + step, y + 2) ||
!Map.isRun(x + step, y - (h >> 1)))
{
isLeft = !isLeft;
setStatus(STATE_RUN);
}
else
x += step;
}
}
//这里定义了每个怪物的动作索引,对应每个怪物的6种状态
byte[] actionArray = new byte[6];
void setActionIndex(byte[] data){
System.arraycopy(data,0,actionArray,0,actionArray.length);
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -