.NET GDI 類比小球彈性碰撞(質點,動能守恒)

來源:互聯網
上載者:User

寫了一個類比小球彈性碰撞的Demo,先貼上來。

 

情境說明:

隨機產生N個小球,隨機初始速度,初始方向,品質,大小,顏色。

每個小球都看做是質點。球和球,球和壁的碰撞都是彈性的(即碰撞後X,Y軸方向變反,大小不變),動能守恒。

左擊滑鼠,停止運動,用箭頭標識當前的運動方向。再單擊則開始運動。

右擊滑鼠,重新初始化情境重新開始。

 

 

 

程式由: 一個Ball類,Game類,和MainForm組成

MainForm提供情境的"畫布", Game控制小球(Ball類)的運動。

 

Form代碼:

using System;<br />using System.Collections.Generic;<br />using System.ComponentModel;<br />using System.Data;<br />using System.Drawing;<br />using System.Linq;<br />using System.Text;<br />using System.Windows.Forms;</p><p>namespace Balls<br />{<br /> public partial class Form1 : Form<br /> {<br /> private Game _game = null;</p><p> public Form1()<br /> {<br /> InitializeComponent();<br /> }</p><p> private void Form1_Load(object sender, EventArgs e)<br /> {</p><p> }</p><p> private void Form1_Shown(object sender, EventArgs e)<br /> {<br /> _game = new Game(this.panel1.CreateGraphics());<br /> _game.Start();<br /> }</p><p> private void timer1_Tick(object sender, EventArgs e)<br /> {<br /> _game.Refresh();<br /> }</p><p> private void panel1_MouseDown(object sender, MouseEventArgs e)<br /> {<br /> if (e.Button == MouseButtons.Left)<br /> {<br /> this.timer1.Enabled = !this.timer1.Enabled;<br /> if (!this.timer1.Enabled)<br /> _game.Pause();<br /> }<br /> else if (e.Button == MouseButtons.Right)<br /> {<br /> DialogResult result =<br /> MessageBox.Show("Restart this game?", "Balls",<br /> MessageBoxButtons.OKCancel, MessageBoxIcon.Question,<br /> MessageBoxDefaultButton.Button2);<br /> if (result == DialogResult.Cancel)<br /> return;<br /> _game = new Game(this.panel1.CreateGraphics());<br /> _game.Start();<br /> }<br /> }</p><p> }<br />}<br />

 

Game代碼:

using System;<br />using System.Collections.Generic;<br />using System.Linq;<br />using System.Text;<br />using System.Drawing;</p><p>namespace Balls<br />{<br /> class Game<br /> {<br /> private List<Ball> balls = new List<Ball>();<br /> private Graphics _g = null;<br /> private int _top = 0;<br /> private int _bottom = 0;<br /> private int _left = 0;<br /> private int _right = 0;<br /> private int _seqenceNo = 0;<br /> private List<Point> locations = new List<Point>();</p><p> public Game(Graphics g)<br /> {<br /> _g = g;<br /> _top = Convert.ToInt32(_g.VisibleClipBounds.Top);<br /> _bottom = Convert.ToInt32(_g.VisibleClipBounds.Bottom);<br /> _left = Convert.ToInt32(_g.VisibleClipBounds.Left);<br /> _right = Convert.ToInt32(_g.VisibleClipBounds.Right);</p><p> int x = Convert.ToInt32(_g.VisibleClipBounds.Width / 4);<br /> int y = Convert.ToInt32(_g.VisibleClipBounds.Height / 5);</p><p> for (int i = 0; i < 4; i++)<br /> {<br /> for (int j = 0; j < 5; j++)<br /> {<br /> Point p = new Point();<br /> p.X = i * x;<br /> p.Y = j * y;<br /> locations.Add(p);<br /> }<br /> }<br /> }</p><p> public void Start()<br /> {<br /> _g.Clear(Color.White);<br /> _seqenceNo = 0;<br /> balls.Clear();<br /> int ballNum = (new Random()).Next(2, 10);<br /> for (int i = 0; i < ballNum; i++)<br /> {<br /> System.Threading.Thread.Sleep(500);<br /> GenerateBall();<br /> }<br /> }</p><p> public void Pause()<br /> {<br /> _g.Clear(Color.White);<br /> foreach (Ball b in balls)<br /> {<br /> b.Move();<br /> b.DrawWithDirection();<br /> }<br /> }</p><p> public void Refresh()<br /> {<br /> _g.Clear(Color.White);<br /> foreach (Ball b in balls)<br /> {<br /> b.Checked = false;<br /> }</p><p> foreach (Ball b in balls)<br /> {<br /> b.Move();<br /> CheckBallAndBall(b);<br /> CheckBallAndWall(b);<br /> b.Draw();<br /> }<br /> }</p><p> private void CheckBallAndBall(Ball b1)<br /> {<br /> foreach (Ball b2 in balls)<br /> {<br /> if (b1.SequenceNo == b2.SequenceNo)<br /> continue;</p><p> int distanceOfBalls = b1.Distance(b2);</p><p> Point newSpeed1 = b1.Speed;<br /> Point newSpeed2 = b2.Speed;<br /> Point newPosition1 = b1.Position;<br /> Point newPosition2 = b2.Position;</p><p> if (distanceOfBalls <= b1.Radius + b2.Radius)<br /> {<br /> int totalWeight = b1.Weight + b2.Weight;<br /> double s1_x = (b1.Weight - b2.Weight) * b1.Speed.X + 2 * b2.Weight * b2.Speed.X;<br /> s1_x = Math.Round(s1_x / totalWeight, MidpointRounding.AwayFromZero);</p><p> double s1_y = (b1.Weight - b2.Weight) * b1.Speed.Y + 2 * b2.Weight * b2.Speed.Y;<br /> s1_y = Math.Round(s1_y / totalWeight, MidpointRounding.AwayFromZero);</p><p> double s2_x = (b2.Weight - b1.Weight) * b2.Speed.X + 2 * b1.Weight * b1.Speed.X;<br /> s2_x = Math.Round(s2_x / totalWeight, MidpointRounding.AwayFromZero);</p><p> double s2_y = (b2.Weight - b1.Weight) * b2.Speed.Y + 2 * b1.Weight * b1.Speed.Y;<br /> s2_y = Math.Round(s2_y / totalWeight, MidpointRounding.AwayFromZero);</p><p> newSpeed1.X = Convert.ToInt32(s1_x);<br /> newSpeed1.Y = Convert.ToInt32(s1_y);<br /> newSpeed2.X = Convert.ToInt32(s2_x);<br /> newSpeed2.Y = Convert.ToInt32(s2_y);<br /> b1.Speed = newSpeed1;<br /> b2.Speed = newSpeed2;</p><p> int val = b1.Radius + b2.Radius - distanceOfBalls;<br /> int x = Convert.ToInt32(val * Math.Abs(Math.Cos(b1.Angle)));<br /> int y = Convert.ToInt32(val * Math.Abs(Math.Sin(b1.Angle)));<br /> if (newSpeed1.X != 0)<br /> x = x * newSpeed1.X / Math.Abs(newSpeed1.X);<br /> else<br /> x = x * -1;</p><p> if (newSpeed1.Y != 0)<br /> y = y * newSpeed1.Y / Math.Abs(newSpeed1.Y);<br /> else<br /> y = y * -1;</p><p> newPosition1.X = newPosition1.X + x;<br /> newPosition1.Y = newPosition1.Y + y;<br /> b1.Position = newPosition1;</p><p> b1.Move();<br /> b2.Move();<br /> if (b1.Distance(b2) <= b1.Radius + b2.Radius)<br /> {<br /> Console.WriteLine("=======");<br /> Console.WriteLine(b1.ToString());<br /> Console.WriteLine(b2.ToString());<br /> Console.WriteLine("=======");<br /> }<br /> }<br /> }</p><p> }</p><p> private void CheckBallAndWall(Ball b)<br /> {<br /> Point newSpeed = b.Speed;<br /> Point newPosition = b.Position;</p><p> if (b.Position.X < _left || b.Position.X + b.Radius * 2 > _right)<br /> {<br /> newSpeed.X = b.Speed.X * (-1);<br /> if (b.Position.X < _left)<br /> newPosition.X = _left;<br /> if (b.Position.X + b.Radius * 2 > _right)<br /> newPosition.X = _right - b.Radius * 2;<br /> }</p><p> if (b.Position.Y < _top || b.Position.Y + b.Radius * 2 > _bottom)<br /> {<br /> newSpeed.Y = b.Speed.Y * (-1);<br /> if (b.Position.Y < _top)<br /> newPosition.Y = _top;<br /> if (b.Position.Y + b.Radius * 2 > _bottom)<br /> newPosition.Y = _bottom - b.Radius * 2;<br /> }</p><p> b.Speed = newSpeed;<br /> b.Position = newPosition;<br /> }</p><p> private void GenerateBall()<br /> {<br /> Random rdm = new Random();</p><p> int radius = rdm.Next(10, 30);<br /> int weight = rdm.Next(10, 50);<br /> int red = rdm.Next(0, 255);<br /> int green = rdm.Next(0, 255);<br /> int blue = rdm.Next(0, 255);<br /> Color color = Color.FromArgb(red, green, blue);</p><p> int locationIdx = rdm.Next(0, locations.Count - 1);<br /> Point p = locations[locationIdx];<br /> locations.Remove(p);</p><p> Point speed = new Point();<br /> speed.X = Convert.ToInt32(rdm.Next(-200, 200) / 20);<br /> speed.Y = Convert.ToInt32(rdm.Next(-200, 200) / 20);</p><p> _seqenceNo = _seqenceNo + 1;</p><p> Ball ball = new Ball(_g, p, speed, radius, weight, color, _seqenceNo);<br /> balls.Add(ball);<br /> }</p><p> }<br />}

 

Ball代碼:

using System;<br />using System.Collections.Generic;<br />using System.Linq;<br />using System.Text;<br />using System.Drawing;</p><p>namespace Balls<br />{<br /> class Ball<br /> {<br /> private Graphics _g;<br /> private Point _position;<br /> private Point _corePosition;<br /> private int _radius;<br /> private Point _speed;<br /> private int _weight;<br /> private Color _color;<br /> private double _angle;<br /> private int _sequenceNo;<br /> private bool _checked;</p><p> public bool Checked<br /> {<br /> get { return _checked; }<br /> set { _checked = value; }<br /> }</p><p> public int SequenceNo<br /> {<br /> get { return _sequenceNo; }<br /> }</p><p> public Point CorePosition<br /> {<br /> get { return _corePosition; }<br /> }</p><p> public Point Position<br /> {<br /> get { return _position; }<br /> set<br /> {<br /> _position = value;<br /> _corePosition.X = _position.X + _radius;<br /> _corePosition.Y = _position.Y + _radius;<br /> }<br /> }</p><p> public Point Speed<br /> {<br /> get { return _speed; }<br /> set<br /> {<br /> _speed = value;<br /> _angle = Math.Atan2(0 - _speed.Y, _speed.X) * (180 / Math.PI);<br /> if (_angle < 0)<br /> _angle = 360 + _angle;<br /> }<br /> }</p><p> public int Radius<br /> {<br /> get { return _radius; }<br /> }</p><p> public double Angle<br /> {<br /> get { return _angle; }<br /> }</p><p> public int Weight<br /> {<br /> get { return _weight; }<br /> }</p><p> public Ball(Graphics g, Point position, Point speed, int radius, int weight, Color color, int seqNo)<br /> {<br /> _sequenceNo = seqNo;<br /> _g = g;<br /> _radius = radius;<br /> _position = position;</p><p> _corePosition = new Point();<br /> _corePosition.X = _position.X + _radius;<br /> _corePosition.Y = _position.Y + _radius;</p><p> _speed = speed;<br /> _weight = weight;<br /> _color = color;<br /> _angle = Math.Atan2(0 - _speed.Y, _speed.X) * (180 / Math.PI);</p><p> if (_angle < 0)<br /> _angle = 360 + _angle;<br /> DrawWithDirection();<br /> }</p><p> public void Draw()<br /> {<br /> _g.DrawEllipse(new Pen(Color.Black, 3), _position.X, _position.Y, 2 * _radius, 2 * _radius);<br /> _g.FillEllipse(new SolidBrush(_color), _position.X, _position.Y, 2 * _radius, 2 * _radius);<br /> }</p><p> public void DrawWithDirection()<br /> {<br /> Draw();<br /> DisplayDirection(this._corePosition, _angle, 20);<br /> }</p><p> private void DisplayDirection(Point p, double angle, int length)<br /> {<br /> Point p2 = DrawLineByAngle(p, angle, length);</p><p> if (angle < 180)<br /> {<br /> DrawLineByAngle(p2, 180 + angle - 30, 5);<br /> DrawLineByAngle(p2, 180 + angle + 30, 5);<br /> }<br /> else if (angle == 180)<br /> {<br /> DrawLineByAngle(p2, 180 + angle - 30, 5);<br /> DrawLineByAngle(p2, -180 + angle + 30, 5);<br /> }<br /> else<br /> {<br /> DrawLineByAngle(p2, -180 + angle - 30, 5);<br /> DrawLineByAngle(p2, -180 + angle + 30, 5);<br /> }<br /> }</p><p> private Point DrawLineByAngle(Point p1, double a, int length)<br /> {<br /> double angle = Math.Abs(a);<br /> if (angle > 360)<br /> angle -= 360;</p><p> double radians = angle * (Math.PI / 180);</p><p> int offsetX = Math.Abs(Convert.ToInt32(length * Math.Cos(radians)));<br /> int offsetY = Math.Abs(Convert.ToInt32(length * Math.Sin(radians)));</p><p> Point p2 = new Point();<br /> p2 = p1;</p><p> if (angle >= 0 && angle <= 90)<br /> {<br /> p2.X = p1.X + offsetX;<br /> p2.Y = p1.Y - offsetY;<br /> }<br /> else if (angle > 90 && angle <= 180)<br /> {<br /> p2.X = p1.X - offsetX;<br /> p2.Y = p1.Y - offsetY;<br /> }<br /> else if (angle > 180 && angle <= 270)<br /> {<br /> p2.X = p1.X - offsetX;<br /> p2.Y = p1.Y + offsetY;<br /> }<br /> else if (angle > 270 && angle <= 360)<br /> {<br /> p2.X = p1.X + offsetX;<br /> p2.Y = p1.Y + offsetY;<br /> }</p><p> _g.DrawLine(new Pen(Color.Red), p1, p2);<br /> return p2;<br /> }</p><p> public void Move()<br /> {<br /> _position.X = _position.X + _speed.X;<br /> _position.Y = _position.Y + _speed.Y;<br /> _corePosition.X = _position.X + _radius;<br /> _corePosition.Y = _position.Y + _radius;<br /> }</p><p> public int Distance(Ball b2)<br /> {<br /> double value1 = Math.Pow((_corePosition.X - b2.CorePosition.X), 2);<br /> double value2 = Math.Pow((_corePosition.Y - b2.CorePosition.Y), 2);<br /> double distance = Math.Sqrt(value1 + value2);<br /> int distanceOfBalls = Convert.ToInt32(Math.Round(distance, MidpointRounding.AwayFromZero));<br /> return distanceOfBalls;<br /> }</p><p> public int NextDistance(Ball b2)<br /> {<br /> double value1 = Math.Pow((_corePosition.X + _speed.X - b2.CorePosition.X + b2.Speed.X), 2);<br /> double value2 = Math.Pow((_corePosition.Y + _speed.Y - b2.CorePosition.Y + b2.Speed.Y), 2);<br /> double distance = Math.Sqrt(value1 + value2);<br /> int distanceOfBalls = Convert.ToInt32(Math.Round(distance, MidpointRounding.AwayFromZero));<br /> return distanceOfBalls;<br /> }</p><p> public override string ToString()<br /> {<br /> return "Ball(" + _sequenceNo.ToString() + ") M:" + _weight.ToString() + ",R:" + _radius.ToString() + ",S_X:" + _speed.X + ",S_Y:" + _speed.Y;<br /> }<br /> }<br />}<br />

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.