Category :
工作 Post on 2010/03/12 08:27 by
yinowl 评论(18)
技术:
J2me高级开发工程师(技术管理岗)
游戏工具高级开发工程师(技术管理岗)
3D高级开发工程师
iPhone开发工程师
QA/QC:
初级技术测试工程师(白盒/黑盒)
高级产品测试工程师(黑盒)
测试主管(管理岗)
美术:
原画
3D美术
像素美术
引用地址:
注意: 该地址仅在今日23:59:59之前有效
Category :
工作 Post on 2008/07/16 00:13 by
yinowl 评论(21)
在(四)的最后,我们给出了主角的按键处理,从代码中大致可以看出,我们的spirit类通过action方法向人物传达指令。人物在接收到指令后,切换不同的状态,然后游戏每一帧循环中执行人物的AI逻辑,根据状态更新相应的动画。大致就是这样的。
我们先把action方法添加到Spirit类中
public void action(int newstate){
if(timeCount==0){
speedControl=0;
frameCount=0;
action_change(newstate);
}
else{//如果是下面这几种情况的话,立即中止当前动画,转换新的状态,进行新的动画
if(((state==MOVER || state==MOVEL || state==MOVE) && newstate!=state)
|| (newstate==DEAD && state!=DEAD) || (newstate==FAINT && state!=FAINT)){
timeCount=0;
action(newstate);
}
}
}
timeCount是一个动画计时器,当一个动画开始时,timeCount是这个动画持续时间值,然后随游戏帧减小,为0时也就是动画播放结束的时候。frameCount保存了当前动画的帧数,speedControl是速度的控制,对应下面的TIMESFRAME,看到后面的代码,马上就会明白他们的具体作用了。
然后来看看action_change方法:
int TIMESFRAME=60/TheBallCanvas.TIME_PER_FRAME;
int GIdx;
public void action_change(int newstate){
state = newstate;
switch(newstate){
case STAND:
timeCount = 1;//TIMESFRAME;
break;
case MOVEL:
case MOVER:
direct = newstate;//人物的方向和方向的状态值是对应的,-1为左,1为右,注意没有break
case MOVE:
timeCount = TIMESFRAME*ACTION_RUN.length;
break;
case ATTACK:
timeCount = TIMESFRAME*ACTION_ATTACK.length;
break;
case UJUMP:
case RJUMP:
case LJUMP:
GIdx=0;//GIdx是重力加速度数组的id
timeCount=TIMESFRAME*ACTION_JUMP.length;
break;
case FAINT:
timeCount = TIMESFRAME*ACTION_FAINT.length;
break;
case DEAD:
timeCount = TIMESFRAME*ACTION_DEAD.length;
break;
case TAKEOFF:
timeCount = TIMESFRAME*ACTION_TAKEOFF.length;
break;
}
actionLength = timeCount;//新动作的总时间长度,单位:帧
}
TIMESFRAME表示动画的每一帧对应游戏的帧数,这里我们用60除以游戏帧的时间,得出游戏循环两帧,动画更新一帧
上面这两个方法都是触发调用的,也就是根据逻辑需要调用方法来改变人物状态。我们还需要一个人物的主逻辑方法,在游戏运行的每一帧中,每一个人物都要进行这个逻辑,更新动画,我们来添加这个方法:
public void actionDo(){
timeCount--;
switch(state){
case STAND:
imageIdx=ACTION_STAND;
break;
case MOVER:
case MOVEL:
this.direct = state;
case MOVE:
if(speedControl == 0){ // 进行图片,位置等变换的时刻
if(frameCount >= ACTION_RUN.length){
frameCount = 0;
}
imageIdx= ACTION_RUN[frameCount];
frameCount ++;
}
if(style==7 && !TheBallCanvas.canAction && TheBallCanvas.theBallSpt.mapX==mapX && TheBallCanvas.theBallSpt.y==y-16){
mapX+= direct*speed/TIMESFRAME;
TheBallCanvas.theBallSpt.mapX=mapX;
}
else if(style==5){
if(TheBallCanvas.gamestate==TheBallCanvas.GAME_GAMING){
mapX+= direct*speed/TIMESFRAME;
y+=direct*speed/TIMESFRAME;
}
}
else
mapX+= direct*speed/TIMESFRAME;
speedControl ++;
if(speedControl >= TIMESFRAME)
speedControl = 0;
break;
}
}
actionDo每帧调用,timeCount递减,然后根据状态更新动画,我们先给出了站立和移动的逻辑。这里的大概结构是这样的,speedControl控制着TIMESFRAME设置的游戏每两帧更新动画,当speedControl等于0的时候可以更新动画,即frameCount增加,然后把对应的图片id传给imageIdx;每一帧speedControl递增,大于等于TIMESFRAME时规零。
我们在Canvas中添加actionDo的调用,在logicGameRun方法的logicFarScene();之后添加:
setScreenPos();
if(theBallSpt.timeCount>0){
theBallSpt.actionDo();
}
else
theBallSpt.state=Spirit.STAND;//当动画结束后回到站立状态
下面来说说人物和地图的坐标结构,每个人物有x,y值,分别表示人物在屏幕上的位置,还有一个mapX表示人物在地图上的位置。我们把主角的位置定在屏幕中间,那么一般情况下mapX就是半个屏幕的宽度,然后给地图也定义了一个mapX变量表示地图最左端距离屏幕最左端的值,没错这个值最大为0,是个负值,并且地图的mapX值根据主角的mapX和x值来确定。在Canvas类中添加setSpiritPos方法来更新地图坐标值
private void setScreenPos() {
if(theBallSpt.mapX>screenW/2 && lvlW-theBallSpt.mapX>screenW/2){
theBallSpt.x=screenW/2;
mapX=theBallSpt.x-theBallSpt.mapX;
}
else if(theBallSpt.mapX<=screenW/2){
mapX=0;
theBallSpt.x=theBallSpt.mapX;
}
else if(lvlW-theBallSpt.mapX<=screenW/2){
mapX=screenW-lvlW+1;
theBallSpt.x=mapX+theBallSpt.mapX;
}
for(int i=0;i<theEmySpt.length;i++)
if(theEmySpt[i]!=null)
theEmySpt[i].x=theEmySpt[i].mapX+mapX;
}
这里大概是这样的:主角的地图前半个屏幕和最后半个屏幕之间的时候,主角的x坐标为定值半个屏幕宽度,并且屏幕mapX值就是主角x值减去主角在地图上的坐标,这个应该很好理解吧;同样但是特殊的,如果主角在地图前半个屏幕,主角的x值就是他的mapX值,此时地图的mapX值就是0;类似的还有主角在最后半个屏幕的情况。相对主角,敌人的坐标就很好算了,就是其在地图上的坐标加上地图的mapX。现在我们运行以下程序,主角应该已经能在地图上走动了,而且地图也会跟着移动。不过奇怪按一下键主角走几步,按住不动,主角不会一直走。我们在logicGameRun方法最后增加
if(keyPressed){
keyGameProcess();
}
同时增加keyRelease方法
public void keyReleased(int keyCode){
keyPressed=false;
if(gamestate==GAME_GAMING)
keyGameProcess();
}
原因应该很明显,当keyPressed为true时,应该不断的调用游戏按键处理。这下按住左右键主角就会一直走了。我们接着来添加主角跳跃的逻辑
为了让跳跃更真实,我们加入了重力加速度,Util里添加一个G数组,根据重力加速度的原理大概算出跳跃时每一帧的高度位移
先在Util中添加这个数组:
public static int[] G={-20,-12,-4,0,4,12,20,24,26,28,28,28,28};
然后在Spirit类的actionDo中添加向上跳跃的逻辑
case UJUMP:
if(speedControl == 0){ // 进行图片,位置等变换的时刻
if(frameCount >= ACTION_JUMP.length){
frameCount = 0;
}
imageIdx= ACTION_JUMP[frameCount];
frameCount ++;
}
if(frameCount>1 && frameCount<9){
y+= JUMPIDX*Util.G[frameCount-2]/TIMESFRAME;
TheBallCanvas.baseLine=TheBallCanvas.screenY+TheBallCanvas.screenH-(TheBallCanvas.deltaY+y)*2/3;
}
speedControl ++;
if(speedControl >= TIMESFRAME)
speedControl = 0;
break;
这里有个JUMPIDX,标识不同跳跃的类型,我们可以先加上定义
static int JUMPIDX=1;
然后添加左跳和右跳:
case LJUMP:
case RJUMP:
this.direct = state-Spirit.UJUMP;//更新方向
if(speedControl == 0){ // 进行图片,位置等变换的时刻
if(frameCount >= ACTION_JUMP.length){
frameCount = 0;
}
imageIdx= ACTION_JUMP[frameCount];
frameCount ++;
GIdx++;
}
if(frameCount>1 && frameCount<8){//这里要用GIdx,因为左右跳会出现从高处往下跳,用frameCount会超出范围
mapX+=(direct*speed/TIMESFRAME);
y+= JUMPIDX*Util.G[GIdx-2]/TIMESFRAME;
TheBallCanvas.baseLine=TheBallCanvas.screenY+TheBallCanvas.screenH-(TheBallCanvas.deltaY+y)*2/3;
}
if(y>=0){//如果掉落深渊,其实就是屏幕下边缘,转入死亡状态,也就是摔死了
y=0;
if(TheBallCanvas.lvlMap[0][mapX/TheBallCanvas.mapCellL]<0){
GIdx=0;
action(DEAD);
break;
}
}
if(mapX<TheBallCanvas.lvlW && TheBallCanvas.lvlMap[-y/TheBallCanvas.mapCellL][mapX/TheBallCanvas.mapCellL]>=0){//这里是一个简单的物理碰撞判断,如果出现平地就停在那格上
for(int x=mapX>>4,y=-this.y/TheBallCanvas.mapCellL;y<TheBallCanvas.lvlMap.length;y++){
if(TheBallCanvas.lvlMap[y][x]<0){
this.y=-(y<<4);
break;
}
}
TheBallCanvas.baseLine=TheBallCanvas.screenY+TheBallCanvas.screenH-(TheBallCanvas.deltaY+y)*2/3;
imageIdx=12;
timeCount=0;
action(TAKEOFF);//左右跳和上跳不同,不一定是上去下来对称的,所以落地时要转入落地状态
break;
}
else if(frameCount==7){//从高处掉落,当到达第7帧后要反复重复6,7帧
JUMPIDX=1;
frameCount=6;
timeCount+=TIMESFRAME;
}
speedControl ++;
if(speedControl >= TIMESFRAME)
speedControl = 0;
break;
这段代码的说明就嵌入在代码里,应该没什么特别的地方。这里出现了TAKEOFF的状态,我们就继续添加这个状态的case,结构都是一样的:
case TAKEOFF:
if(speedControl == 0){ // 进行图片,位置等变换的时刻
if(frameCount >= ACTION_TAKEOFF.length){
frameCount = 0;
}
imageIdx= ACTION_TAKEOFF[frameCount];
frameCount ++;
}
speedControl ++;
if(speedControl >= TIMESFRAME)
speedControl = 0;
break;
引用地址:
注意: 该地址仅在今日23:59:59之前有效
Category :
闲聊 | Post on 2008/05/09 09:25 by
yinowl |
评论(10)
博客文章预告《J2ME游戏开发教程:初级动作闯关类-《混血王子与梦幻国度》(五) 》:持续更新
近期活动好好休息休息,补齐很久没有发的图片和文章
我们的推荐日志请打开“星标日志”欢迎大家多多留言哦
引用地址:
注意: 该地址仅在今日23:59:59之前有效
Category :
相濡以沫 | Post on 2010/05/22 01:03 by
明明 |
评论(12)
决定了么?
是的!
引用地址:
注意: 该地址仅在今日23:59:59之前有效
Category :
相濡以沫 | Post on 2010/05/18 22:16 by
明明 |
评论(13)
痛……接受……改变……重生……
加油!加油!
引用地址:
注意: 该地址仅在今日23:59:59之前有效
Category :
工作 | Post on 2010/04/30 14:25 by
yinowl |
评论(17)
引用地址:
注意: 该地址仅在今日23:59:59之前有效
Category :
闲聊 | Post on 2010/02/27 10:41 by
yinowl |
评论(9)
包括电子书,电纸书,平板电脑
过去有太多的行业交叉电子产品失败,如果放到互联网发达的今天,更重要的是放到移动互联网快速发展的今天,或许就不会失败了。
很早以前就呼声很高的平板电脑,在iPhone/iTouch成功后,或许已经用事实证明了时机已经成熟,不再停留在预期、分析和冒险。
3G和wifi的广泛应用和移动应用商店的新模式给用户带来了非常便利的体验。
这样看来,硬件成本是唯一的问题了,但硬件成本又从来不会成为最后的问题。
引用地址:
注意: 该地址仅在今日23:59:59之前有效
Category :
闲聊 | Post on 2010/02/26 08:18 by
明明 |
评论(4)
休息了整整4个月,回到公司上班,心情复杂(以下省略1000字……)
努力增肥,胡吃海塞了4个月,仅仅长了4斤,还不是净增长(冬天的衣服多),离目标还很远很远啊……
4个月中,药罐子煮坏掉2个,好在肠胃好多了。一回到公司又开始拉肚子,果真是公司的水有问题?
调整心态,调整心态……
引用地址:
注意: 该地址仅在今日23:59:59之前有效