zoukankan      html  css  js  c++  java
  • MMORPG programming in Silverlight Tutorial (9)KeyFrame Animation

        In the last chapter, I introduce A* algorithm, so the object can find the shortest path itself, I print the path array on the canvas. This chapter I will animate the object to pass through the path array from start to the end.

        We still use the demo in the last chapter.

        1st, add an ellipse named player into the canvas, as the sprite in our demo.

    //the sprite
    private Ellipse player;
    
    private void InitPlayer()
    {
        player = new Ellipse()
        {
            Fill = new SolidColorBrush(Colors.Blue),
            Width = gridSize,
            Height = gridSize
        };
    
        Carrier.Children.Add(player);
    
        //init start point
        Canvas.SetLeft(player, gridSize);
        Canvas.SetTop(player, 5 * gridSize);
    }

        2nd, add obstruction in the method InitMatrix.

    void InitMatrix()
    {
        for (int y = 0; y < matrix.GetUpperBound(1); y++)
        {
            for (int x = 0; x < matrix.GetUpperBound(0); x++)
            {
                matrix[x, y] = 1;
            }
        }
    
        for (int y = 3; y < 30; y++)
        {
            matrix[3, y] = 0;
            rect = new Rectangle();
            rect.Fill = new SolidColorBrush(Colors.Red);
            rect.Width = gridSize;
            rect.Height = gridSize;
            Carrier.Children.Add(rect);
            Canvas.SetLeft(rect, 3 * gridSize);
            Canvas.SetTop(rect, y * gridSize);
        }
        for (int y = 3; y < 20; y++)
        {
            matrix[24, y] = 0;
            rect = new Rectangle();
            rect.Fill = new SolidColorBrush(Colors.Red);
            rect.Width = gridSize;
            rect.Height = gridSize;
            Carrier.Children.Add(rect);
            Canvas.SetLeft(rect, 24 * gridSize);
            Canvas.SetTop(rect, y * gridSize);
        }
        for (int i = 0; i < 18; i++)
        {
            matrix[i, 12] = 0;
            rect = new Rectangle();
            rect.Fill = new SolidColorBrush(Colors.Red);
            rect.Width = gridSize;
            rect.Height = gridSize;
            Carrier.Children.Add(rect);
            Canvas.SetLeft(rect, i * gridSize);
            Canvas.SetTop(rect, 12 * gridSize);
        }
        for (int i = 12; i < 17; i++)
        {
            matrix[17, i] = 0;
            rect = new Rectangle();
            rect.Fill = new SolidColorBrush(Colors.Red);
            rect.Width = gridSize;
            rect.Height = gridSize;
            Carrier.Children.Add(rect);
            Canvas.SetLeft(rect, 17 * gridSize);
            Canvas.SetTop(rect, i * gridSize);
        }
        for (int i = 3; i < 18; i++)
        {
            matrix[i, 16] = 0;
            rect = new Rectangle();
            rect.Fill = new SolidColorBrush(Colors.Red);
            rect.Width = gridSize;
            rect.Height = gridSize;
            Carrier.Children.Add(rect);
            Canvas.SetLeft(rect, i * gridSize);
            Canvas.SetTop(rect, 16 * gridSize);
        }
    }

        3rd, execute these 2 methods above in our constructor.

    public MainPage()
    {
        InitializeComponent();
    
        //init matrix
        InitMatrix();
    
        //init sprite
        InitPlayer();   
    }

        4. Add keyframe in the method Carrier_MouseLeftButtonDown, I have written enough comment in the code, which can help you understand more clearly.

    private void Carrier_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        Point p = e.GetPosition(Carrier);
    
        //scale down the coordinate of start and end
        int start_x = (int)Canvas.GetLeft(player) / gridSize;
        int start_y = (int)Canvas.GetTop(player) / gridSize;
        start = new Point(start_x, start_y);
    
        int end_x = (int)p.X / gridSize;
        int end_y = (int)p.Y / gridSize;
        end = new Point(end_x, end_y);
    
    
        //use A* algorithm
        IPathFinder pathFinder = new PathFinderFast(matrix);
        pathFinder.Formula = HeuristicFormula.Manhattan; 
        pathFinder.HeavyDiagonals = true;
        pathFinder.HeuristicEstimate = 0;
    
        var path = pathFinder.FindPath(start, end);
        if (path == null)
        {
            MessageBox.Show("The path doesn't exist");
            return;
        }
    
        //define keyframe array
        Point[] framePosition = new Point[path.Count];
    
        for (int i = path.Count - 1; i >= 0; i--)
        {
            //fill keyframe array from the start of the path array, and enlarge the coordinate
            framePosition[path.Count - 1 - i] = new Point(path[i].X * gridSize, path[i].Y * gridSize);
        }
    
        //create storyboard
        Storyboard storyboard = new Storyboard();
        int cost = 100; //spend 100ms per grid(20*20)
    
        //create animation frame by frame in X-coordinate
        DoubleAnimationUsingKeyFrames keyFramesAnimationX = new DoubleAnimationUsingKeyFrames();
    
        //all the spending time = path.Count * cost
        keyFramesAnimationX.Duration = new Duration(TimeSpan.FromMilliseconds(path.Count * cost));
    
        Storyboard.SetTarget(keyFramesAnimationX, player);
        Storyboard.SetTargetProperty(keyFramesAnimationX, new PropertyPath("(Canvas.Left)"));
    
        //create animation frame by frame in Y-coordinate
        DoubleAnimationUsingKeyFrames keyFramesAnimationY = new DoubleAnimationUsingKeyFrames();
        keyFramesAnimationY.Duration = new Duration(TimeSpan.FromMilliseconds(path.Count * cost));
    
        Storyboard.SetTarget(keyFramesAnimationY, player);
        Storyboard.SetTargetProperty(keyFramesAnimationY, new PropertyPath("(Canvas.Top)"));
    
        for (int i = 0; i < framePosition.Count(); i++)
        {
            //add keyframe in X-coordinate
            LinearDoubleKeyFrame keyFrame = new LinearDoubleKeyFrame();
    
            keyFrame.Value = i == 0 ? Canvas.GetLeft(player) : framePosition[i].X;
            keyFrame.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(cost * i));
            keyFramesAnimationX.KeyFrames.Add(keyFrame);
    
            //add keyframe in Y-coordinate
            keyFrame = new LinearDoubleKeyFrame();
            keyFrame.Value = i == 0 ? Canvas.GetTop(player) : framePosition[i].Y;
            keyFrame.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(cost * i));
            keyFramesAnimationY.KeyFrames.Add(keyFrame);
        }
    
        storyboard.Children.Add(keyFramesAnimationX);
        storyboard.Children.Add(keyFramesAnimationY);
    
        //start the storyboard
        storyboard.Begin();
    
        //trace the moving by white points
        for (int i = path.Count - 1; i >= 0; i--)
        {
            rect = new Rectangle()
            {
                Fill = new SolidColorBrush(Colors.White),
                Width = 5,
                Height = 5
            };
    
            Carrier.Children.Add(rect);
    
            Canvas.SetLeft(rect, path[i].X * gridSize);
            Canvas.SetTop(rect, path[i].Y * gridSize);
        }
    }

        More or less, the principle is that, when we click on the canvas, we will start and end point at once.

        We add keyFramesAnimationX and keyFramesAnimationY in the storyboard, they are Timeline in the x and y coordinate.

    //create animation frame by frame in X-coordinate
    DoubleAnimationUsingKeyFrames keyFramesAnimationX = new DoubleAnimationUsingKeyFrames();
    
    //all the spending time = path.Count * cost
    keyFramesAnimationX.Duration = new Duration(TimeSpan.FromMilliseconds(path.Count * cost));
    
    Storyboard.SetTarget(keyFramesAnimationX, player);
    Storyboard.SetTargetProperty(keyFramesAnimationX, new PropertyPath("(Canvas.Left)"));
    
    //create animation frame by frame in Y-coordinate
    DoubleAnimationUsingKeyFrames keyFramesAnimationY = new DoubleAnimationUsingKeyFrames();
    keyFramesAnimationY.Duration = new Duration(TimeSpan.FromMilliseconds(path.Count * cost));
    
    Storyboard.SetTarget(keyFramesAnimationY, player);
    Storyboard.SetTargetProperty(keyFramesAnimationY, new PropertyPath("(Canvas.Top)"));

    ……(ignore some code)

    storyboard.Children.Add(keyFramesAnimationX);
    storyboard.Children.Add(keyFramesAnimationY);

        Then we add keyframe in the Timeline:

    for (int i = 0; i < framePosition.Count(); i++)
    {
        //add keyframe in X-coordinate
        LinearDoubleKeyFrame keyFrame = new LinearDoubleKeyFrame();
    
        keyFrame.Value = i == 0 ? Canvas.GetLeft(player) : framePosition[i].X;
        keyFrame.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(cost * i));
        keyFramesAnimationX.KeyFrames.Add(keyFrame);
    
        //add keyframe in Y-coordinate
        keyFrame = new LinearDoubleKeyFrame();
        keyFrame.Value = i == 0 ? Canvas.GetTop(player) : framePosition[i].Y;
        keyFrame.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(cost * i));
        keyFramesAnimationY.KeyFrames.Add(keyFrame);
    }

        Finally, we print all the path element in white color. Notice that the first element in the path array is the end point of the path., so we need to iterate the path array from end to start.

       

        OK, press Ctrl+F5, click on the canvas. The original point of the ellipse is A. When I click on the point B, the ellipse begins moving. When the ellipse passes through the point C, we click on the other point D, the ellipse will change direction at once, it dues to the features of timeline and keyframe.

    image

    Summary: This chapter introduce how to integrate A* algorithm and KeyFrame, so we can control the animation freely.

        Next chapter, I will implement how to add all these techniques in our game engine. Please focus on it.

        Chinese friend, you can also visit this Chinese blog if you feel difficult to read English, http://www.cnblogs.com/alamiye010/archive/2009/06/17/1505346.html, part of my article is base on it.

        Demo download: http://silverlightrpg.codeplex.com/releases/view/40978

  • 相关阅读:
    【转载】著名黑客雷蒙评价几种编程语言
    【GUI开发】Swing的一本极好的入门教材
    【数据结构】数组操作(HighArrayApp.java)
    数据另存为CSV档案(也是一种excel档案)【2】------自主选择路径
    TIniFile实现打开窗体后还原用户之前的配置的功能
    sql server内连接(inner join)、外连接(left outer join、right outer join、full outer join)、记录合并(union、union all)
    @指针、Cardinal()、Integer()、指针取值解析
    关于php编程的一些小技巧
    数据库操作,同时更新多条数据
    省份,城市,地区------三级联动菜单//要加注释
  • 原文地址:https://www.cnblogs.com/Jax/p/1674841.html
Copyright © 2011-2022 走看看