【问题标题】:2D movement animation for sprites using one sprite sheet with four directions使用一个具有四个方向的精灵表的精灵的 2D 运动动画
【发布时间】:2015-01-14 19:17:29
【问题描述】:

我正在制作一个用于学习目的的 2D 塔防游戏,并且无法让敌人(精灵)在移动时面向正确的方向。

这里是如何构建地图以便澄清:

http://i.imgur.com/ivO8kWe.png

这是我用来测试的精灵表:

http://i.imgur.com/2h4fSL3.png

左上角的第一个精灵是第 0 帧,右边的下一个是第 1 帧,依此类推。如您所见,精灵已经在寻找错误的方向。地图有一个起点(顶部的第一个棕色瓷砖)和一个终点(最后一个棕色瓷砖),只有棕色瓷砖是可步行的,所以它会计算起点和终点精灵为到达终点而行走的最短有效路径。

说,每个生成的精灵都会有一个预先确定的行走路径,从那里我尝试通过检查最后的 X 或 Y 位置以及 X 或 Y 当前位置来找到精灵所面对的方向,我选择我将用于行走动画的精灵表的哪一行。例如,假设精灵向南移动,它应该使用精灵表底部的精灵(第 15 到 19 帧),但它不起作用。

这是我用于敌人的动画类:

 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 帧开始,当它到达它固定的第三个瓷砖并面向正确的方向。

【问题讨论】:

  • 什么 不起作用?
  • lastPosition 何时/何地设置?如果它在基础中完成,那么您将永远不会真正投入到您的动画方法中,因为position 将等于lastPosition。是什么让我认为是这种情况,因为您的屏幕截图显示它正在使用第一行帧,我想它会默认使用。
  • 它会走路和改变方向,但它看起来很随机,它也不遵循精灵表的帧顺序。我在基类(Sprite)的更新方法中设置了最后一个位置。我在一条直线上做了一个测试级别:i.imgur.com/3FsGhuY.png 它从第 0 帧开始朝左,但是当它到达第三个棕色瓷砖时,它会将位置固定在右侧。
  • 这很奇怪...int line = (int)((float)currentFrame / (float)Columns); 想象一下,如果你在第 9 帧。你会返回 1.8f,然后转换为整数 2。就个人而言,我会将其转换为 2D 或锯齿状数组,其中第一个 x 是您的动画部分,y 将是您的实际帧。所以不是第 9 帧,而是类似于frame[line, currentFrame],其中currentFrame 将是 4。
  • @TyCobb 在这种情况下,整数截断会产生正确的答案(即答案不是从 1.8 舍入到 2),但实际上没有必要将这些变量转换为浮点数。整数除法就足够了。

标签: c# xna 2d


【解决方案1】:

您没有检查当前帧是否低于所需动画循环的最小值;您只是根据最大值检查它。此外,您的代码中可能存在一些重复,可能应该将其排除以使其更易于阅读和使用。

我会用一个方法替换你所有的 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);

【讨论】:

  • 成功了!现在看起来很简单,你不知道我花了多少时间试图自己解决它。非常感谢先生!
猜你喜欢
  • 2023-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多