1. 程式人生 > >【APP GAME KIT】能碰到障礙物的移動小人

【APP GAME KIT】能碰到障礙物的移動小人

網上下載的例子執行以後會出現以下情況:

① 小人移動速度較慢。

② 小人移動動畫不準確,即移動時候小人的雙腳並不會運動。反而是站著的時候雙腳會走。

③ 小人向下或向下移動的時候同時按左鍵或右鍵,小人方向不會發生改變,但是會向左或向右移動;小人向左或向右移動的時候按上下鍵,小人方向會發生改變。這是因為 if 和 else if 導致的按鍵事故。


原版程式碼:

// Includes, namespace and prototypes
#include "template.h"
using namespace AGK;
app App;

// Function prototypes
void loadTiles();
void displayTiles();
void updateAlecX(float);
void updateAlecY(float);

// Constants for the screen resolution
const int SCREEN_WIDTH  = 640;
const int SCREEN_HEIGHT = 480;

// Constants for the image numbers
const int GRASS  = 1;
const int PATH   = 2;
const int PATHNE = 3;
const int PATHNW = 4;
const int PATHSE = 5;
const int PATHSW = 6;
const int TREENW = 7;
const int TREENE = 8;
const int TREESW = 9;
const int TREESE = 10;
const int ROCK   = 11;

// Constants for the tile image sizes
const int TILE_WIDTH  = 64;
const int TILE_HEIGHT = 48;

// Constants for the tile map size declarators
const int TILE_ROWS = 10;
const int TILE_COLS = 10;

// Constants for the Alec sprite sheet
const int ALEC_IMAGE        = 12;  // Texture atlas image index
const int ALEC_SPRITE       = 100; // Alec's sprite index
const int ALEC_FRAME_WIDTH  = 40;  // Alec's frame width
const int ALEC_FRAME_HEIGHT = 75;  // Alec's frame height
const int ALEC_FRAME_COUNT  = 16;  // Alec's frame count
const int EAST_START        = 1;   // First frame for going east
const int EAST_END          = 4;   // Last frame for going east
const int NORTH_START       = 5;   // First frame for going north
const int NORTH_END         = 8;   // Last frame for going north
const int SOUTH_START       = 9;   // First frame for going south
const int SOUTH_END         = 12;  // Last frame for going south
const int WEST_START        = 13;  // First frame for going west
const int WEST_END          = 16;  // Last frame for going west
const int ALEC_FPS          = 5;   // Alec's frames per second
const int ANIMATION_LOOP    = 1;   // To make Alec loop
const float ALEC_STARTING_X = 0;   // Alec's starting X coordinate
const float ALEC_STARTING_Y = 150; // Alec's starting Y coordinate

// Constants for Alec's direction
const int NORTH   = 1;
const int SOUTH = 2;
const int EAST  = 3;
const int WEST  = 4;

// The tile map
int g_tileMap[TILE_ROWS][TILE_COLS] =
{ {GRASS, GRASS, GRASS, GRASS, GRASS,  GRASS,  GRASS,  GRASS,  GRASS, GRASS},
  {GRASS, GRASS, GRASS, ROCK,  GRASS,  GRASS,  GRASS,  GRASS,  GRASS, GRASS},
  {GRASS, GRASS, GRASS, GRASS, GRASS,  GRASS,  GRASS,  PATHNW, PATH,  PATH },
  {GRASS, GRASS, GRASS, GRASS, GRASS,  TREENW, TREENE, PATH,   GRASS, GRASS},
  {PATH,  PATH,  PATH,  PATH,  PATHNE, TREESW, TREESE, PATH,   ROCK,  GRASS},
  {GRASS, GRASS, GRASS, GRASS, PATH,   GRASS,  GRASS,  PATH,   GRASS, GRASS},
  {GRASS, GRASS, GRASS, GRASS, PATHSW, PATH,   PATH,   PATHSE, GRASS, GRASS},
  {GRASS, GRASS, GRASS, GRASS, GRASS,  GRASS,  GRASS,  GRASS,  GRASS, GRASS},
  {GRASS, ROCK,  GRASS, GRASS, GRASS,  GRASS,  GRASS,  GRASS,  GRASS, GRASS},
  {GRASS, GRASS, GRASS, GRASS, GRASS,  GRASS,  GRASS,  GRASS,  GRASS, GRASS}
};

// Variable for Alec's direction
int g_alecDirection = EAST;

// Begin app, called once at the start
void app::Begin( void )
{
   // Set the window title.
   agk::SetWindowTitle("Walking Alec");

   // Set the virtual resolution.
   agk::SetVirtualResolution(SCREEN_WIDTH, SCREEN_HEIGHT);

   // Load the texture atlas.
   agk::LoadImage(ALEC_IMAGE, "Alec/Alec.png");

   // Create the sprite using the texture atlas as the image.
   agk::CreateSprite(ALEC_SPRITE, ALEC_IMAGE);

   // Make sure Alec is displayed on top of the tile sprites.
   agk::SetSpriteDepth(ALEC_SPRITE, 0);

   // Set Alec's starting position.
   agk::SetSpritePosition(ALEC_SPRITE, ALEC_STARTING_X, 
                          ALEC_STARTING_Y);

   // Set the sprite animation.
   agk::SetSpriteAnimation(ALEC_SPRITE, ALEC_FRAME_WIDTH, 
                           ALEC_FRAME_HEIGHT, ALEC_FRAME_COUNT);

   // Load the tile images.
   loadTiles();

   // Create the tile sprites and display them.
   displayTiles();
}

// Main loop, called every frame
void app::Loop ( void )
{
   // Get the state of the direction keys.
   float directionX = agk::GetDirectionX();
   float directionY = agk::GetDirectionY();

   // If the right or left arrow keys are pressed,
   // update Alec's X coordinate.
   if (directionX != 0)
   {
      updateAlecX(directionX);
   }

   // If the up or down arrow keys are pressed,
   // update Alec's Y coordinate.
   if (directionY != 0)
   {
      updateAlecY(directionY);
   }

   // Refresh the screen.
    agk::Sync();
}

// Called when the app ends
void app::End ( void )
{
}

// The loadTiles function loads the images that will be
// used for tiles.
void loadTiles()
{
   agk::LoadImage(GRASS,  "Alec/Grass.png");
   agk::LoadImage(PATH,   "Alec/Path.png");
   agk::LoadImage(PATHNE, "Alec/PathNE.png");
   agk::LoadImage(PATHNW, "Alec/PathNW.png");
   agk::LoadImage(PATHSE, "Alec/PathSE.png");
   agk::LoadImage(PATHSW, "Alec/PathSW.png");
   agk::LoadImage(TREENE, "Alec/TreeNE.png");
   agk::LoadImage(TREENW, "Alec/TreeNW.png");
   agk::LoadImage(TREESE, "Alec/TreeSE.png");
   agk::LoadImage(TREESW, "Alec/TreeSW.png");
   agk::LoadImage(ROCK,   "Alec/Rock.png");
}

// The displayTiles function displays the tiles, as
// specified by the tile map.
void displayTiles()
{
   // Variables for the tile coordinates
   float x = 0, y = 0;

   // Variable to temporarily hold a sprite index
   int spriteIndex;
   
   // Display all the tiles specified in the map.
   for (int r = 0; r < TILE_ROWS; r++)
   {
      // Set x to 0.
      x = 0;

      // Display all the tiles in this row.
      for (int c = 0; c < TILE_COLS; c++)
      {
         // Create a sprite for this tile.
         spriteIndex = agk::CreateSprite(g_tileMap[r][c]);

         // Set the tile's position.
         agk::SetSpritePosition(spriteIndex, x, y);

         // Update the X coordinate for the next tile.
         x += TILE_WIDTH;
      }
   
      // Increase y for the next row.
      y += TILE_HEIGHT;
    }
}

// The updateAlecX function turns Alec either east or west,
// depending on which arrow key is being pressed, and moves
// him to his new X coordinate.
void updateAlecX(float directionX)
{
   float alecX,   // Alec's current X position
         newX;    // Alec's new X coordinate

   // Get Alec's X coordinate
   alecX = agk::GetSpriteX(ALEC_SPRITE);

   // Which key was pressed? Right or left?
   if (directionX > 0)
   {
      // Turn Alec east
      agk::PlaySprite(ALEC_SPRITE, ALEC_FPS, 
                      ANIMATION_LOOP, 
                      EAST_START, EAST_END);

      // Save Alec's current direction.
      g_alecDirection = EAST;

      // Calculate Alec's new X coordinate.
      newX = alecX + 1;
   }
   else if (directionX < 0)
   {
      // Turn Alec west
      agk::PlaySprite(ALEC_SPRITE, ALEC_FPS, 
                      ANIMATION_LOOP, 
                      WEST_START, WEST_END);

      // Save Alec's current direction.
      g_alecDirection = WEST;

      // Calculate Alec's new X coordinate
      newX = alecX - 1;
   }

   // Move Alec
   agk::SetSpriteX(ALEC_SPRITE, newX);
}

// The updateAlecY function turns Alec either north or south,
// depending on which arrow key is being pressed, and moves
// him to his new Y coordinate.
void updateAlecY(float directionY)
{
   float alecY,   // Alec's current Y position
         newY;    // Alec's new Y coordinate

   // Get Alec's Y coordinate
   alecY = agk::GetSpriteY(ALEC_SPRITE);

   // Which key was pressed? Up or down?
   if (directionY < 0)
   {
      // Turn Alec north
      agk::PlaySprite(ALEC_SPRITE, ALEC_FPS, 
                      ANIMATION_LOOP, 
                      NORTH_START, NORTH_END);

      // Save Alec's current direction.
      g_alecDirection = NORTH;

      // Calculate Alec's new Y coordinate.
      newY = alecY - 1;
   }
   else if (directionY > 0)
   {
      // Turn Alec south
      agk::PlaySprite(ALEC_SPRITE, ALEC_FPS, 
                      ANIMATION_LOOP,
                      SOUTH_START, SOUTH_END);

      // Save Alec's current direction.
      g_alecDirection = SOUTH;

      // Calculate Alec's new Y coordinate.
      newY = alecY + 1;
   }

   // Move Alec
   agk::SetSpriteY(ALEC_SPRITE, newY);
}


自己嘗試獨立打了一次程式碼:

#include "template.h"
using namespace AGK;
app App;

//tile圖片編號
const int GRASS = 1;//草坪圖片
const int PATH = 2;//石徑滿
const int PATHNE = 3;//石徑左拐下
const int PATHNW = 4;//石徑右拐下
const int PATHSE = 5;//石徑左拐上
const int PATHSW = 6;//石徑上拐右
const int TREENW = 7;//樹左上部分
const int TREENE = 8;//樹右上部分
const int TREESW = 9;//樹坐下部分
const int TREESE = 10;//樹右下部分
const int ROCK = 11;//石頭


//每張矩圖的大小
const int TILE_WIDTH = 64;
const int TILE_HEIGHT = 48;

//地圖矩陣n x n
const int TILE_ROWS = 10;
const int TILE_COLS = 10;

//螢幕大小
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;

int g_tileMap[TILE_ROWS][TILE_COLS] = {
	GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,
	GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,
	GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,PATHNW,PATH,PATH,
	GRASS,GRASS,GRASS,GRASS,GRASS,TREENW,TREENE,PATH,GRASS,GRASS,
	PATH ,PATH ,PATH ,PATH ,PATHNE,TREESW,TREESE,PATH,ROCK,GRASS,
	GRASS,GRASS,GRASS,GRASS,PATH,GRASS,GRASS,PATH,GRASS,GRASS,
	GRASS,GRASS,GRASS,GRASS,PATHSW,PATH,PATH,PATHSE,GRASS,GRASS,
	GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,
	GRASS,ROCK,GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,
	GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,
};

//四個方向
const int NORTH = 1;
const int SOUTH = 2;
const int EAST = 3;
const int WEST = 4;

//人物引數
const int ALEC_IMAGE = 12;//ALEC圖片
const int ALEC_SPRITE = 100;//ALEC精靈
const int ALEC_FRAME_WIDTH = 40;//alec人物圖片寬度
const int ALEC_FRAME_HEIGHT = 75;//alec人物圖片高度
const int ALEC_FRAME_COUNT = 16;//總共16幅圖片
const int EAST_START = 1;//向東開始
const int EAST_END = 4;//向東結束
const int NORTH_START = 5;//向北開始
const int NORTH_END = 8;//向北結束
const int SOUTH_START = 9;//向南開始
const int SOUTH_END = 12;//向南結束
const int WEST_START = 13;//向西開始
const int WEST_END = 16;//向西結束
const int ALEC_FPS = 5;//幀率
const int ANIMATION_LOOP = 1;//迴圈
const int WALK_DISTANCE = 4;
const int STANDING = 0;//站立
const int WALKING = 1;//行走
const float ALEC_STARTING_X = 0;//X座標
const float ALEC_STARTING_Y = 150;//Y座標
int g_alecDirection = EAST;//人物朝向
int g_state = STANDING;//人物當前狀態

//測試資料
const int X = 1;
const int Y = 2;

//Function
void loadTiles();//載入tile圖片
void displayTiles();//輸出地圖
void updateAlecX(float);
void updateAlecY(float);
int direction(float,float);
bool checkCollision(float x,float y);

void app::Begin(void)
{
	agk::SetVirtualResolution (SCREEN_WIDTH, SCREEN_HEIGHT);
	agk::SetWindowTitle("Walking Alec");
	agk::LoadImage(ALEC_IMAGE,"Alec/Alec.png");
	agk::CreateSprite(ALEC_SPRITE,ALEC_IMAGE);
	agk::SetSpriteDepth(ALEC_SPRITE,0);
	agk::SetSpritePosition(ALEC_SPRITE,ALEC_STARTING_X,ALEC_STARTING_Y);
	agk::SetSpriteAnimation(ALEC_SPRITE,ALEC_FRAME_WIDTH,ALEC_FRAME_HEIGHT,ALEC_FRAME_COUNT);


	agk::CreateText(X,agk::Str(ALEC_STARTING_X));
	agk::SetTextSize(X,24);
	agk::SetTextPosition(X,SCREEN_WIDTH - agk::GetTextTotalWidth(X),0);

	agk::CreateText(Y,agk::Str(ALEC_STARTING_Y));
	agk::SetTextSize(Y,24);
	agk::SetTextPosition(Y,SCREEN_WIDTH - agk::GetTextTotalWidth(Y),agk::GetTextTotalHeight(X)+5);

	agk::CreateText(200,"10");
	agk::SetTextSize(200,24);
	agk::SetTextPosition(200,SCREEN_WIDTH - agk::GetTextTotalWidth(200),agk::GetTextTotalHeight(X)+5+agk::GetTextTotalHeight(Y)+5);

	agk::CreateText(201,"10");
	agk::SetTextSize(201,24);
	agk::SetTextPosition(201,SCREEN_WIDTH - agk::GetTextTotalWidth(201),agk::GetTextTotalHeight(X)+5+agk::GetTextTotalHeight(Y)+5+agk::GetTextTotalHeight(200)+5);

	agk::CreateText(202,"1");
	agk::SetTextSize(202,24);
	agk::SetTextPosition(202,0,400
		);


	loadTiles();
	displayTiles();
}

void app::Loop (void)
{
	float directionX = agk::GetDirectionX();
	float directionY = agk::GetDirectionY();
	agk::SetTextString(X,agk::Str(agk::GetSpriteX(ALEC_SPRITE)));
	agk::SetTextString(Y,agk::Str(agk::GetSpriteY(ALEC_SPRITE)));
	int isLEFTRIGHT = direction(directionX,directionY);
	switch(isLEFTRIGHT){
		case 0:
			if(g_state==WALKING){
				g_state = STANDING;
				switch(g_alecDirection){
					case NORTH:
						agk::PlaySprite(ALEC_SPRITE,1,ANIMATION_LOOP,6,6);
						break;
					case SOUTH:
						agk::PlaySprite(ALEC_SPRITE,1,ANIMATION_LOOP,10,10);
						break;
					case WEST:
						agk::PlaySprite(ALEC_SPRITE,1,ANIMATION_LOOP,14,14);
						break;
					case EAST:
						agk::PlaySprite(ALEC_SPRITE,1,ANIMATION_LOOP,2,2);
						break;
				}
			}
			break;
		case 1:
			updateAlecX(directionX);
			break;
		case 2:
			updateAlecY(directionY);
		break;
	}
	agk::Sync();
}


void app::End (void)
{

}

void loadTiles(){//載入tile圖片
	agk::LoadImage(GRASS,"Alec/Grass.png");
	agk::LoadImage(PATH,"Alec/Path.png");
	agk::LoadImage(PATHNE,"Alec/PathNE.png");
	agk::LoadImage(PATHNW,"Alec/PathNW.png");
	agk::LoadImage(PATHSE,"Alec/PathSE.png");
	agk::LoadImage(PATHSW,"Alec/PathSW.png");
	agk::LoadImage(TREENW,"Alec/TreeNW.png");
	agk::LoadImage(TREENE,"Alec/TreeNE.png");
	agk::LoadImage(TREESW,"Alec/TreeSW.png");
	agk::LoadImage(TREESE,"Alec/TreeSE.png");
	agk::LoadImage(ROCK,"Alec/Rock.png");
}

void displayTiles(){
	float x = 0, y = 0;
	int spriteIndex;
	for(int r = 0;r<TILE_ROWS;r++){
		x = 0;
		for(int c = 0 ; c < TILE_COLS; c++){
			spriteIndex = agk::CreateSprite(g_tileMap[r][c]);
			agk::SetSpritePosition(spriteIndex,x,y);
			x+=TILE_WIDTH;
		}
		y+=TILE_HEIGHT;
	}
}

void updateAlecX(float directionX){
	float alecX,newX;
	alecX = agk::GetSpriteX(ALEC_SPRITE);
	if(directionX>0){
		if(g_alecDirection !=EAST || g_state==STANDING){
			agk::PlaySprite(ALEC_SPRITE,ALEC_FPS,ANIMATION_LOOP,EAST_START,EAST_END);
			g_alecDirection = EAST;
			g_state = WALKING;
		}
		if(alecX+ALEC_FRAME_WIDTH <= SCREEN_WIDTH-WALK_DISTANCE && checkCollision(alecX+WALK_DISTANCE,agk::GetSpriteY(ALEC_SPRITE))){
			newX = alecX + WALK_DISTANCE;
			agk::SetSpriteX(ALEC_SPRITE,newX);
		}
	}
	else if(directionX < 0){
		if(g_alecDirection !=WEST || g_state==STANDING){
			agk::PlaySprite(ALEC_SPRITE,ALEC_FPS,ANIMATION_LOOP,WEST_START,WEST_END);
			g_alecDirection = WEST;
			g_state = WALKING;
		}
		if(alecX>=WALK_DISTANCE && checkCollision(alecX-WALK_DISTANCE,agk::GetSpriteY(ALEC_SPRITE))){
			newX = alecX - WALK_DISTANCE;
			agk::SetSpriteX(ALEC_SPRITE,newX);
		}
	}
	
}

void updateAlecY(float directionY){
	float alecY,newY;
	alecY = agk::GetSpriteY(ALEC_SPRITE);
	if(directionY > 0){
		if(g_alecDirection != SOUTH || g_state==STANDING){
			agk::PlaySprite(ALEC_SPRITE,ALEC_FPS,ANIMATION_LOOP,SOUTH_START,SOUTH_END);
			g_alecDirection = SOUTH;
			g_state = WALKING;
		}
		if(alecY+ALEC_FRAME_HEIGHT <= SCREEN_HEIGHT-WALK_DISTANCE && checkCollision(agk::GetSpriteX(ALEC_SPRITE),alecY+WALK_DISTANCE)){
			newY = alecY + WALK_DISTANCE;
			agk::SetSpriteY(ALEC_SPRITE,newY);
		}
	}
	else if(directionY < 0){
		if(g_alecDirection != NORTH || g_state==STANDING){
			agk::PlaySprite(ALEC_SPRITE,ALEC_FPS,ANIMATION_LOOP,NORTH_START,NORTH_END);
			g_alecDirection = NORTH;
			g_state = WALKING;
		}
		if(alecY>=WALK_DISTANCE && checkCollision(agk::GetSpriteX(ALEC_SPRITE),alecY-WALK_DISTANCE)){
			newY = alecY - WALK_DISTANCE;
			agk::SetSpriteY(ALEC_SPRITE,newY);
		}
	}
}

int direction(float x,float y){
	int c=0,isx = -1;
	if(x!=0){
		c++;
		isx = 1;
	}
	if(y!=0){
		c++;
		isx = 0;
	}
	switch(c){
	case 0:
		return 0;
		break;
	case 1:
		if(isx == 1){
			return 1;
		}
		else if(isx == 0){
			return 2;
		}
		break;
	case 2:
		switch(g_alecDirection){
		case NORTH:
		case SOUTH:
			return 1;
			break;
		case WEST:
		case EAST:
			return 2;
			break;
		}
		break;
	}
	return 0;
}

bool checkCollision(float a,float b){
	int y = (int)((a + ALEC_FRAME_WIDTH/2)/TILE_WIDTH);
	int x = (int)((b + ALEC_FRAME_HEIGHT)/TILE_HEIGHT);
	agk::SetTextString(200,agk::Str(x));
	agk::SetTextString(201,agk::Str(y));
	agk::SetTextString(202,agk::Str(g_tileMap[x][y]));
	if(g_tileMap[x][y]>=7){
		return false;
	}
	return true;
}


做出的修改如下:

① 小人速度加快。

② 增加了阻礙移動的物體。

③ 按下左右鍵時候按下上下鍵的時候互相切換,反之亦然。這樣雖然避免了if和else if導致有一個鍵先行判斷而另一個鍵後判斷的情況。但是人物移動的時候兩個方向快速切換也是影響視覺效果。最好還是繪製四個斜方向的移動小人動畫。

④ 小人移動的時候雙腳擺動,站立時停止擺動。


實際上的速度要比gif圖片要快的多。中間的圖上右上右動的極快。

agk有一點致命的地方是並不支援中文文字。中文文字輸出會出現亂碼。所以想要中文只能用圖片的形式輸出了。