您的位置:首页 > 编程语言 > C#

c# AntiSocialRobots 源码分析

2014-03-02 11:28 232 查看
需求:鼠标点击画布,画出 Robot 对象,每个 Robot 可以自己寻找路径,保证最后所有的 Robot 在画布上均匀分布。

定义 Robot 类

namespace AntisocialRobots
{
    /// <summary>Represents one robot.</summary>
    internal class Robot
    {
        /// <summary>The visual element used to represent the robot on the screen.</summary>
        public Ellipse Element;

        /// <summary>The current location of the robot within the room.</summary>
        public RoomPoint Location;

        /// <summary>The game frame in which this robot was last moved.</summary>
        public int LastMovedFrame;
    }
}


定义 RoomPosition 类

namespace AntisocialRobots
{
    internal struct RoomPoint
    {
        public int X;
        public int Y;

        public RoomPoint(int x, int y)
        {
            X = x;
            Y = y; 
        }
        public double DistanceTo(RoomPoint other) { return Math.Sqrt(Square(other.X-X)+Square(other.Y-Y));}
        public double AngleTo(RoomPoint other) { return Math.Atan2(other.Y - Y, other.X - X); }
        public static double Square(double n) { return n*n; }

    }
}


鼠标点击位置转换为画布位置

RoomPoint MousePositionToRoomPoint(MouseEventArgs e)
        {
            Point pt = e.GetPosition(Room);
            return new RoomPoint((int)(pt.X * ROOM_SIZE), (int)(pt.Y * ROOM_SIZE));
        }


模拟 Robot 移动一个位置,对于 Robot 对象来说,它并不需要知道自己在屏幕上的实际位置,它只应当知道自己在房间中(画布)的位置,因此模拟移动一个位置,应当只改变 Robot 对象的 RoomPosition.

void SimulateOneStep(Robot r)
        {
            RoomPoint ptR = r.Location;
            double vectorX = 0, vectorY = 0;

            foreach (Robot s in _robots)
            {
                if (r == s) continue;
                RoomPoint ptS = s.Location;
                double inverseSquareDistance = 1.0 / RoomPoint.Square(ptR.DistanceTo(ptS));
                double angle = ptR.AngleTo(ptS);
                vectorX -= inverseSquareDistance * Math.Cos(angle);
                vectorY -= inverseSquareDistance * Math.Sin(angle);
            }
            double degrees = Math.Atan2(vectorY, vectorX) * 180 / Math.PI;

            degrees += 22.5;
            while (degrees < 0) degrees += 360;
            while (degrees >= 360) degrees -= 360;

            int direction = (int)(degrees * 8 / 360);

            if ((direction == 7) || (direction == 0) || (direction == 1))
                ptR.X = Math.Min(ptR.X + 1, ROOM_SIZE - 1);
            else if ((direction == 3) || (direction == 4) || (direction == 5))
                ptR.X = Math.Max(ptR.X - 1, 0);

            if ((direction == 1) || (direction == 2) || (direction == 3))
                ptR.Y = Math.Min(ptR.Y + 1, ROOM_SIZE - 1);
            else if ((direction == 5) || (direction == 6) || (direction == 7))
                ptR.Y = Math.Max(ptR.Y - 1, 0);

            if (((ptR.X != r.Location.X) || (ptR.Y != r.Location.Y)) && _roomCells[ptR.X, ptR.Y] == null)
            {
                _roomCells[r.Location.X, r.Location.Y] = null;
                _roomCells[ptR.X, ptR.Y] = r;
                r.Location = new RoomPoint(ptR.X, ptR.Y);
            }
        }


Robot 在屏幕上的位置由主程序来控制,根据 RoomPosition 来转换。这样虽然进行了两次位置转换,但是剥离了业务和实现之间的耦合,使代码更简洁。

Robot 屏幕位置计算:

void SetRobotElementPosition(Robot robot, RoomPoint pt)
        {
            Canvas.SetLeft(robot.Element,((double)pt.X)/ROOM_SIZE);
            Canvas.SetTop(robot.Element,((double)pt.Y)/ROOM_SIZE);
        }
现在问题是必须一直执行后台计算来保证 Robot 在画布上画出来之后,能够自己去计算位置,并在画布上画出新的位置。也就是说不能只在画 Robot 这个动作的完成的时候计算位置,而是在程序运行期间都要计算所有 Robot 的位置。使用线程可以完成这个任务:

Action recomputeAndRedraw = null;
            recomputeAndRedraw = delegate
            {
                Dispatcher.BeginInvoke((Action)delegate
                {
                    PerformSimulationStep();
                    recomputeAndRedraw();
                }, DispatcherPriority.Background);
            };
            Loaded += delegate
            {
                _framesPerSecondStopwatch.Start();
                recomputeAndRedraw();
            };


界面加载起来之后, action 就会一直在后台执行,计算 Robot 位置,并在界面上画出来。

技巧:

private TaskFactory _uiTasks;

_uiTasks=new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext());

//在 Action 中执行异步操作。

_uiTasks.StartNew(()=>{});

_uiTasks.StartNew(delegate{});
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: