使用一个 sprite sheet 四个方向的 sprite 的 2D 运动动画
2D movement animation for sprites using one sprite sheet with four directions
我正在制作一个用于学习目的的2D塔防游戏,并且无法使敌人(精灵)在移动时面向正确的方向。
以下是地图的构建方式以供说明:
http://i.imgur.com/ivO8kWe.png
这是我用来测试的精灵 sheet:
http://i.imgur.com/2h4fSL3.png
左上角的第一个精灵是第0帧,右边下一个是第1帧,依此类推。如您所见,精灵已经看向了错误的方向。该地图有一个起点(顶部的第一个棕色瓷砖)和一个终点(最后一个棕色瓷砖在最后),只有棕色瓷砖是可步行的,所以用起点和终点计算精灵为了到达终点而行走的最短有效路径。
说,每个生成的精灵都会有一条预先确定的行走路径,从中我尝试通过检查最后的 X 或 Y 位置和 X 或 Y 当前位置来找到精灵面对的方向,我选择精灵 sheet 的哪一行用于行走动画。例如,假设精灵正在向南移动,它应该使用精灵底部的精灵 sheet(第 15 到 19 帧),但它不起作用。
这是动画 class 我正在为敌人使用:
public class AnimatedSprite : Sprite
{
public int Lines { get; set; }
public int Columns { get; set; }
protected int currentFrame;
protected int totalFrames;
protected int timeSinceLastFrame = 0;
protected int milisecondsPerFrame = 50;
public AnimatedSprite(Texture2D texture, int lines, int columns, Vector2 position)
: base ( texture, position)
{
this.texture = texture;
this.position = position;
Lines = lines;
Columns = columns;
totalFrames = Lines * Columns;
}
public override void Update(GameTime gameTime)
{
base.Update(gameTime);
//Here i check if the sprite sheet have more than 1 line because if it have,
//it must use a different update method.
if (Lines > 1)
{
// Down
if (lastPostion.Y < position.Y)
{
AnimateDown(gameTime);
}
// Up
if (position.Y < lastPosition.Y)
{
AnimateUp(gameTime);
}
// Right
if (position.X > lastPosition.X)
{
AnimateRight(gameTime);
}
// Left
if (position.X < lastPosition.X)
{
AnimateLeft(gameTime);
}
}
if (Lines == 1) {
timeSinceLastFrame += gameTime.ElapsedGameTime.Milliseconds;
if (timeSinceLastFrame > milisecondsPerFrame)
{
timeSinceLastFrame -= milisecondsPerFrame;
currentFrame++;
if (currentFrame == totalFrames)
{
currentFrame = 0;
}
}
}
center = new Vector2(position.X + texture.Width / Columns, position.Y + texture.Height / Lines);
}
public void AnimateUp(GameTime gameTime)
{
timeSinceLastFrame += gameTime.ElapsedGameTime.Milliseconds;
if (timeSinceLastFrame > milisecondsPerFrame)
{
timeSinceLastFrame -= milisecondsPerFrame;
currentFrame++;
if (currentFrame > 14)
currentFrame = 10;
}
}
public void AnimateDown(GameTime gameTime)
{
timeSinceLastFrame += gameTime.ElapsedGameTime.Milliseconds;
if (timeSinceLastFrame > milisecondsPerFrame)
{
timeSinceLastFrame -= milisecondsPerFrame;
currentFrame++;
if (currentFrame > 19)
currentFrame = 15;
}
}
public void AnimateLeft(GameTime gameTime)
{
timeSinceLastFrame += gameTime.ElapsedGameTime.Milliseconds;
if (timeSinceLastFrame > milisecondsPerFrame)
{
timeSinceLastFrame -= milisecondsPerFrame;
currentFrame++;
if (currentFrame > 4)
currentFrame = 0;
}
}
public void AnimateRight(GameTime gameTime)
{
timeSinceLastFrame += gameTime.ElapsedGameTime.Milliseconds;
if (timeSinceLastFrame > milisecondsPerFrame)
{
timeSinceLastFrame -= milisecondsPerFrame;
currentFrame++;
if (currentFrame > 9)
currentFrame = 5;
}
}
public override void Draw(SpriteBatch spriteBatch)
{
int width = texture.Width / Columns;
int height = texture.Height / Lines;
int line = (int)((float)currentFrame / (float)Columns);
int column = currentFrame % Columns;
Rectangle originRectangle = new Rectangle(width * column, height * line, width, height);
Rectangle destinationRectangle = new Rectangle((int)position.X, (int)position.Y, width, height);
spriteBatch.Draw(texture, destinationRectangle, originRectangle, Color.White);
}
}
}
编辑:我在直线水平(左边的起点)进行了测试,它开始面向左侧(第 0 帧),但是当它到达第三个棕色瓷砖时,它固定并面向正确的方向:
i.imgur.com/3FsGhuY.png
Edit2:我在所有四个方向(开始向下和向上,从右开始向左,反之亦然)用直线级别进行了测试,并且在所有这些方向上,它都从第 0 帧开始,当它到达它固定的第三个瓷砖并面向正确的方向。
您没有检查当前帧是否低于所需动画循环的最小值;您只是根据最大值检查它。此外,您的代码中有一些重复项可能应该被排除,以使其更易于阅读和使用。
我会用一个方法替换您所有的 AnimateXXXX 方法:
public void AnimateLoop(GameTime gameTime, int loopFirstFrame, int loopLastFrame)
{
timeSinceLastFrame += gameTime.ElapsedGameTime.Milliseconds;
if (timeSinceLastFrame > milisecondsPerFrame)
{
timeSinceLastFrame -= milisecondsPerFrame;
currentFrame++;
}
if (currentFrame > loopLastFrame || currentFrame < loopFirstFrame)
currentFrame = loopFirstFrame;
}
然后这样称呼他们:
// Down
if (lastPostion.Y < position.Y)
AnimateLoop(gameTime, 15, 19);
// Up
if (position.Y < lastPosition.Y)
AnimateLoop(gameTime, 10, 14);
// Right
if (lastPosition.X < position.X)
AnimateLoop(gameTime, 5, 9);
// Left
if (position.X < lastPosition.X)
AnimateLoop(gameTime, 0, 4);
我正在制作一个用于学习目的的2D塔防游戏,并且无法使敌人(精灵)在移动时面向正确的方向。
以下是地图的构建方式以供说明:
http://i.imgur.com/ivO8kWe.png
这是我用来测试的精灵 sheet:
http://i.imgur.com/2h4fSL3.png
左上角的第一个精灵是第0帧,右边下一个是第1帧,依此类推。如您所见,精灵已经看向了错误的方向。该地图有一个起点(顶部的第一个棕色瓷砖)和一个终点(最后一个棕色瓷砖在最后),只有棕色瓷砖是可步行的,所以用起点和终点计算精灵为了到达终点而行走的最短有效路径。
说,每个生成的精灵都会有一条预先确定的行走路径,从中我尝试通过检查最后的 X 或 Y 位置和 X 或 Y 当前位置来找到精灵面对的方向,我选择精灵 sheet 的哪一行用于行走动画。例如,假设精灵正在向南移动,它应该使用精灵底部的精灵 sheet(第 15 到 19 帧),但它不起作用。
这是动画 class 我正在为敌人使用:
public class AnimatedSprite : Sprite
{
public int Lines { get; set; }
public int Columns { get; set; }
protected int currentFrame;
protected int totalFrames;
protected int timeSinceLastFrame = 0;
protected int milisecondsPerFrame = 50;
public AnimatedSprite(Texture2D texture, int lines, int columns, Vector2 position)
: base ( texture, position)
{
this.texture = texture;
this.position = position;
Lines = lines;
Columns = columns;
totalFrames = Lines * Columns;
}
public override void Update(GameTime gameTime)
{
base.Update(gameTime);
//Here i check if the sprite sheet have more than 1 line because if it have,
//it must use a different update method.
if (Lines > 1)
{
// Down
if (lastPostion.Y < position.Y)
{
AnimateDown(gameTime);
}
// Up
if (position.Y < lastPosition.Y)
{
AnimateUp(gameTime);
}
// Right
if (position.X > lastPosition.X)
{
AnimateRight(gameTime);
}
// Left
if (position.X < lastPosition.X)
{
AnimateLeft(gameTime);
}
}
if (Lines == 1) {
timeSinceLastFrame += gameTime.ElapsedGameTime.Milliseconds;
if (timeSinceLastFrame > milisecondsPerFrame)
{
timeSinceLastFrame -= milisecondsPerFrame;
currentFrame++;
if (currentFrame == totalFrames)
{
currentFrame = 0;
}
}
}
center = new Vector2(position.X + texture.Width / Columns, position.Y + texture.Height / Lines);
}
public void AnimateUp(GameTime gameTime)
{
timeSinceLastFrame += gameTime.ElapsedGameTime.Milliseconds;
if (timeSinceLastFrame > milisecondsPerFrame)
{
timeSinceLastFrame -= milisecondsPerFrame;
currentFrame++;
if (currentFrame > 14)
currentFrame = 10;
}
}
public void AnimateDown(GameTime gameTime)
{
timeSinceLastFrame += gameTime.ElapsedGameTime.Milliseconds;
if (timeSinceLastFrame > milisecondsPerFrame)
{
timeSinceLastFrame -= milisecondsPerFrame;
currentFrame++;
if (currentFrame > 19)
currentFrame = 15;
}
}
public void AnimateLeft(GameTime gameTime)
{
timeSinceLastFrame += gameTime.ElapsedGameTime.Milliseconds;
if (timeSinceLastFrame > milisecondsPerFrame)
{
timeSinceLastFrame -= milisecondsPerFrame;
currentFrame++;
if (currentFrame > 4)
currentFrame = 0;
}
}
public void AnimateRight(GameTime gameTime)
{
timeSinceLastFrame += gameTime.ElapsedGameTime.Milliseconds;
if (timeSinceLastFrame > milisecondsPerFrame)
{
timeSinceLastFrame -= milisecondsPerFrame;
currentFrame++;
if (currentFrame > 9)
currentFrame = 5;
}
}
public override void Draw(SpriteBatch spriteBatch)
{
int width = texture.Width / Columns;
int height = texture.Height / Lines;
int line = (int)((float)currentFrame / (float)Columns);
int column = currentFrame % Columns;
Rectangle originRectangle = new Rectangle(width * column, height * line, width, height);
Rectangle destinationRectangle = new Rectangle((int)position.X, (int)position.Y, width, height);
spriteBatch.Draw(texture, destinationRectangle, originRectangle, Color.White);
}
}
}
编辑:我在直线水平(左边的起点)进行了测试,它开始面向左侧(第 0 帧),但是当它到达第三个棕色瓷砖时,它固定并面向正确的方向:
i.imgur.com/3FsGhuY.png
Edit2:我在所有四个方向(开始向下和向上,从右开始向左,反之亦然)用直线级别进行了测试,并且在所有这些方向上,它都从第 0 帧开始,当它到达它固定的第三个瓷砖并面向正确的方向。
您没有检查当前帧是否低于所需动画循环的最小值;您只是根据最大值检查它。此外,您的代码中有一些重复项可能应该被排除,以使其更易于阅读和使用。
我会用一个方法替换您所有的 AnimateXXXX 方法:
public void AnimateLoop(GameTime gameTime, int loopFirstFrame, int loopLastFrame)
{
timeSinceLastFrame += gameTime.ElapsedGameTime.Milliseconds;
if (timeSinceLastFrame > milisecondsPerFrame)
{
timeSinceLastFrame -= milisecondsPerFrame;
currentFrame++;
}
if (currentFrame > loopLastFrame || currentFrame < loopFirstFrame)
currentFrame = loopFirstFrame;
}
然后这样称呼他们:
// Down
if (lastPostion.Y < position.Y)
AnimateLoop(gameTime, 15, 19);
// Up
if (position.Y < lastPosition.Y)
AnimateLoop(gameTime, 10, 14);
// Right
if (lastPosition.X < position.X)
AnimateLoop(gameTime, 5, 9);
// Left
if (position.X < lastPosition.X)
AnimateLoop(gameTime, 0, 4);