十一、移动角色
在屏幕上移动角色——无论是人、动物、机器人还是车辆——是引人注目的游戏中最重要的部分之一。如果你试图创造一个在游戏中自由移动的角色,你可能会遇到一些问题。
本章将介绍帮助你移动角色的解决方案。本章中的解决方案包括让角色奔跑,以及在角色移动时改变角色动画。
第一种方法帮助你在屏幕上向四个方向移动你的角色。其余的解决方案帮助您以不同的速度移动角色,并在角色移动时为其设置动画。
11.1 向四个方向移动角色
问题
屏幕上的角色不会移动。
解决办法
使用游戏循环来控制角色的移动。
它是如何工作的
这个解决方案要求你追踪玩家想要角色移动到哪里,然后将这个意图转换到模型矩阵的 x 或 y 轴。换句话说,一旦你捕捉到玩家想要移动的位置,你就可以使用一个switch...case
语句来确定在模型矩阵中平移哪个轴,从而相应地移动屏幕上的角色。
您分三步完成此解决方案。您需要确定玩家想要移动的方向,然后创建一个保存该值的标志,最后使用该值来移动屏幕上的角色。第一步是捕捉玩家想要移动的方向。我们将通过使用SimpleOnGestureListener()
来实现这一点。
玩家将向左、向右、向上或向下滑动来指示角色应该朝哪个方向跑(想想类似寺庙跑步风格的输入系统)。在游戏的主意图中,实例化一个新的SimpleOnGestureListener()
,如清单 11-1 所示。
清单 11-1 。SimpleOnGestureListener()
GestureDetector.SimpleOnGestureListener gestureListener = new GestureDetector.SimpleOnGestureListener(){
@Override
public boolean onDown(MotionEventarg0) {
//TODO Auto-generated method stub
return false;
}
@Override
public boolean onFling(MotionEvente1, MotionEvente2, float velocityX,
float velocityY) {
float leftMotion = e1.getX() - e2.getX();
float upMotion = e1.getY() - e2.getY();
float rightMotion = e2.getX() - e1.getX();
float downMotion = e2.getY() - e1.getY();
if((leftMotion == Math.max(leftMotion, rightMotion))&&
(leftMotion>Math.max(downMotion, upMotion)) )
{
}
if((rightMotion == Math.max(leftMotion, rightMotion))&&
(rightMotion>Math.max(downMotion, upMotion) )
{
}
if((upMotion == Math.max(upMotion, downMotion))&&
(upMotion>Math.max(leftMotion, rightMotion)) )
{
}
if((downMotion == Math.max(upMotion, downMotion))&&
(downMotion>Math.max(leftMotion, rightMotion)) )
{
}
return false;
}
@Override
public void onLongPress(MotionEvent e) {
//TODO Auto-generated method stub
}
@Override
public boolean onScroll(MotionEvente1, MotionEvente2, float distanceX,
float distanceY) {
//TODO Auto-generated method stub
return false;
}
@Override
public void onShowPress(MotionEvent e) {
//TODO Auto-generated method stub
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
//TODO Auto-generated method stub
return false;
}
};
注意这个实例化中的四个if
语句。它们代表向左、向右、向上和向下的动作。现在创建一个可以从主意图和游戏循环中访问的int
。根据SimpleOnGestureListener()
检测到的方向设置int
(参见清单 11-2 )。
清单 11-2 。SimpleOnGestureListener()
public static int playeraction = 0;
public static final int PLAYER_MOVE_LEFT = 1;
public static final int PLAYER_MOVE_RIGHT = 2;
public static final int PLAYER_MOVE_UP = 3;
public static final int PLAYER_MOVE_DOWN = 4;
...
if((leftMotion == Math.max(leftMotion, rightMotion)) &&
(leftMotion>Math.max(downMotion, upMotion)) )
{
playeraction = PLAYER_MOVE_LEFT;
}
if((rightMotion == Math.max(leftMotion, rightMotion)) &&
(rightMotion>Math.max(downMotion, upMotion) )
{
playeraction = PLAYER_MOVE_RIGHT;
}
if((upMotion == Math.max(upMotion, downMotion)) &&
(upMotion>Math.max(leftMotion, rightMotion)) )
{
playeraction = PLAYER_MOVE_UP;
}
if((downMotion == Math.max(upMotion, downMotion)) &&
(downMotion>Math.max(leftMotion, rightMotion)) )
{
playeraction = PLAYER_MOVE_DOWN;
}
...
最后,在Renderer
中,创建一个方法来读取你刚刚设置的int
的值,并相应地转换角色的模型矩阵,如清单 11-3 和清单 11-4 所示。
清单 11-3 。movePlayer()
(OpenGL 是 1)
private void movePlayer(GL10gl){
switch(playeraction){
case PLAYER_MOVE_RIGHT:
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glTranslatef(0f, .75f, 0f);
character.draw(gl);
gl.glPopMatrix();
gl.glLoadIdentity();
break;
case PLAYER_MOVE_LEFT:
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glTranslatef(0f, -.75f, 0f);
character.draw(gl);
gl.glPopMatrix();
gl.glLoadIdentity();
break;
case PLAYER_MOVE_UP:
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glTranslatef(.75f, 0f, 0f);
character.draw(gl);
gl.glPopMatrix();
gl.glLoadIdentity();
break;
case PLAYER_MOVE_DOWN:
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glTranslatef(-.75f, 0f, 0f);
character.draw(gl);
gl.glPopMatrix();
gl.glLoadIdentity();
break;
}
}
清单 11-4 。movePlayer()
(OpenGL 是 2/3)
private void movePlayer(GL10gl){
switch(playeraction){
case PLAYER_MOVE_RIGHT:
Matrix.translateM(mTMatrix, 0, 0, .75f, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mTMatrix, 0, mMVPMatrix, 0)
character.draw(mMVPMatrix);
break;
case PLAYER_MOVE_LEFT:
Matrix.translateM(mTMatrix, 0, 0, -.75f, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mTMatrix, 0, mMVPMatrix, 0)
character.draw(mMVPMatrix);
break;
case PLAYER_MOVE_UP:
Matrix.translateM(mTMatrix, 0, .75f, 0, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mTMatrix, 0, mMVPMatrix, 0)
character.draw(mMVPMatrix);
break;
case PLAYER_MOVE_DOWN:
Matrix.translateM(mTMatrix, 0, -.75f, 0, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mTMatrix, 0, mMVPMatrix, 0)
character.draw(mMVPMatrix);
break;
}
}
对glTranslatef()
的调用在清单 11-3 (对于 OpenGL ES 代码)中用粗体突出显示,因为你应该用在你的特定游戏中效果最好的值来转换你的模型矩阵。
11.2 以不同的速度移动角色
问题
游戏角色需要以不同的速度行走和奔跑。
解决办法
使用游戏循环数来决定角色何时应该改变速度。
它是如何工作的
在这个解决方案中,您计算已经执行的游戏循环次数,并使用这个计数来确定角色的速度何时应该改变。例如,您的游戏是以这样的方式构建的,当玩家触摸屏幕的右侧时,角色将向右移动,当玩家触摸屏幕的左侧时,角色将向左移动,当玩家不触摸屏幕时,角色静止不动。如果玩家只是短时间触摸屏幕,你可以使用这种架构让角色行走,如果玩家触摸屏幕的时间更长,就让角色奔跑。
提示 第 5 章概述了设置基于触摸控制的游戏的解决方案。
第一步是创建两个变量,这两个变量可以从游戏中的任何类中读取。第一个变量跟踪已经执行的游戏循环次数,第二个变量跟踪角色的当前速度。
public static final float PLAYER_RUN_SPEED = .15f;
public static int totalGameLoops = 0;
接下来,在Renderer
类中创建一个movePlayer()
方法。到目前为止,这种方法已经在本书的多个解决方案中使用。如果你需要这个方法如何工作的基本解释,请参见第 6 章。
movePlayer()
方法包含一个switch...case
语句,该语句读取玩家的动作并相应地移动角色。修改这个方法来测试执行循环的次数,并在此基础上改变角色的速度(参见清单 11-5 和清单 11-6 )。
清单 11-5 。改变移动速度(OpenGL ES 1)
private void movePlayer(GL10gl){
if (totalGameLoops> 15)
{
PLAYER_RUN_SPEED += .5f;
}
switch(playeraction){
case PLAYER_MOVE_RIGHT:
playercurrentlocation += PLAYER_RUN_SPEED;
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glTranslatef(playercurrentlocation, 0f, 0f);
goodguy.draw(gl);
gl.glPopMatrix();
gl.glLoadIdentity();
break;
case PLAYER_MOVE_LEFT:
playercurrentlocation -= PLAYER_RUN_SPEED;
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glTranslatef(playercurrentlocation, 0f, 0f);
goodguy.draw(gl);
gl.glPopMatrix();
gl.glLoadIdentity();
break;
case PLAYER_STAND:
PLAYER_RUN_SPEED = .15f;
totalGameLoops = 0;
break;
}
}
清单 11-6 。改变移动速度(OpenGL ES 2/3)
private void movePlayer(GL10gl){
if (totalGameLoops> 15)
{
PLAYER_RUN_SPEED += .5f;
}
switch(playeraction){
case PLAYER_MOVE_RIGHT:
playercurrentlocation += PLAYER_RUN_SPEED;
Matrix.translateM(mTMatrix, 0, 0,playercurrentlocation, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mTMatrix, 0, mMVPMatrix, 0)
character.draw(mMVPMatrix);
break;
case PLAYER_MOVE_LEFT:
playercurrentlocation -= PLAYER_RUN_SPEED;
Matrix.translateM(mTMatrix, 0, 0,playercurrentlocation, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mTMatrix, 0, mMVPMatrix, 0)
character.draw(mMVPMatrix);
break;
case PLAYER_STAND:
PLAYER_RUN_SPEED = .15f;
totalGameLoops = 0;
break;
}
}
最后,在Renderer
的onDrawFrame()
方法中,每次执行增加totalGameLoops int
(参见清单 11-7 )。
清单 11-7 。totalGameLoops
public void onDrawFrame(GL10gl) {
...
totalGameLoops +=1;
movePlayer(gl);
...
}
11.3 当角色移动时制作动画
问题
当游戏角色移动时,它看起来不像是在行走。
解决办法
使用 spritesheet 动画使角色在移动时看起来像在行走。
它是如何工作的
这个解决方案将涉及到对您已经广泛使用的movePlayer()
方法进行修改。在模型矩阵被转换后,转换纹理矩阵以显示 spritesheet 中的下一帧。
注意关于使用斜板的解决方案,参见第 6 章。
首先创建一个在所有类中都可见的作用域变量,该变量将用于跟踪 spritesheet 动画的当前帧。
public static float currentrunaniframe = 0f;
接下来,对movePlayer()
方法进行加粗的修改(参见清单 11-8 和 11-9 )。
清单 11-8 。动画角色(OpenGL ES 1)
private void movePlayer(GL10gl){
if (totalGameLoops> 15)
{
PLAYER_RUN_SPEED += .5f;
}
currentrunaniframe += .25f;
if (currentrunaniframe> .75f)
{
currentrunaniframe = .0f;
}
switch(playeraction){
case PLAYER_MOVE_RIGHT:
playercurrentlocation += PLAYER_RUN_SPEED;
scrollBackground1(gl, playeraction);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glTranslatef(playercurrentlocation, 0f, 0f);
gl.glMatrixMode(GL10.GL_TEXTURE);
gl.glLoadIdentity();
gl.glTranslatef(currentrunaniframe,.50f, 0.0f);
goodguy.draw(gl);
gl.glPopMatrix();
gl.glLoadIdentity();
break;
case PLAYER_MOVE_LEFT:
playercurrentlocation -= PLAYER_RUN_SPEED;
scrollBackground1(gl, playeraction);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glTranslatef(playercurrentlocation, 0f, 0f);
gl.glMatrixMode(GL10.GL_TEXTURE);
gl.glLoadIdentity();
gl.glTranslatef(currentrunaniframe,.75f, 0.0f);
goodguy.draw(gl);
gl.glPopMatrix();
gl.glLoadIdentity();
break;
case PLAYER_STAND:
PLAYER_RUN_SPEED = .15f;
totalGameLoops = 0;
break;
}
}
清单 11-9 。制作角色动画(OpenGL ES 2/3)
private void movePlayer(GL10gl){
if (totalGameLoops> 15)
{
PLAYER_RUN_SPEED += .5f;
}
currentrunaniframe += .25f;
if (currentrunaniframe> .75f)
{
currentrunaniframe = .0f;
}
switch(playeraction){
case PLAYER_MOVE_RIGHT:
playercurrentlocation += PLAYER_RUN_SPEED;
Matrix.translateM(mTMatrix, 0, 0, playercurrentlocation, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mTMatrix, 0, mMVPMatrix, 0)
character.draw(mMVPMatrix,currentrunaniframe, .50f );
break;
case PLAYER_MOVE_LEFT:
playercurrentlocation -= PLAYER_RUN_SPEED;
Matrix.translateM(mTMatrix, 0, 0, playercurrentlocation, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mTMatrix, 0, mMVPMatrix, 0)
character.draw(mMVPMatrix,currentrunaniframe, .75f );
break;
case PLAYER_STAND:
PLAYER_RUN_SPEED = .15f;
totalGameLoops = 0;
break;
}
}
清单 11-8 中加粗的if
语句基于四帧动画工作。如果 sprite 工作表中有四帧角色动画,则在四次循环后,动画需要重置为第一帧。if
语句测试当前帧,并在到达第四帧时重置动画。
制作角色动画的关键(如果你在 OpenGL ES 2/3 中工作)是修改你的角色类的draw()
方法,以传入你想要显示的 sprite sheet 图像的 x 和 y 位置(见第 6 章以获得完成这个的详细解决方案)。
最后,修改PLAYER_STAND
案例,将动画从变为静态的“站立”图像。请记住,根据 spritesheet 的设置,该解决方案中显示的坐标可能需要更改(参见清单 11-10 和 11-11 )。
清单 11-10 。PLAYER_STAND
(OpenGL 是 1)
case PLAYER_STAND:
PLAYER_RUN_SPEED = .15f;
totalGameLoops = 0;
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glTranslatef(playercurrentlocation, 0f, 0f);
gl.glMatrixMode(GL10.GL_TEXTURE);
gl.glLoadIdentity();
gl.glTranslatef(.25f,.25f, 0.0f);
goodguy.draw(gl);
gl.glPopMatrix();
gl.glLoadIdentity();
break;
清单 11-11 。PLAYER_STAND
(OpenGL 是 2/3)
case PLAYER_STAND:
PLAYER_RUN_SPEED = .15f;
totalGameLoops = 0;
playercurrentlocation -= PLAYER_RUN_SPEED;
Matrix.translateM(mTMatrix, 0, 0, playercurrentlocation, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mTMatrix, 0, mMVPMatrix, 0)
character.draw(mMVPMatrix,.25f, .25f );
break;
版权属于:月萌API www.moonapi.com,转载请注明出处