Desi nu e chiar domeniul meu de activitate, o sa incerc sa prezint cateva elemente de animatie – basic, cum s-ar zice. Scriu acest articol – despre care sper sa fie primul dintr-o serie de articole despre animatie – in primu rand pentru mine, din dorinta de a ma juca de-a animatia.
Ce imi propun sa realizez in aceasta prima incercare de animatie: sa desenez o bila, cat de cat realistica, si sa o plimb intr-un cadru. In final as vrea sa afisez pe ecran ceva de genul urmator:
Pentru inceput creez un nou proiect, de tip Windows Forms Application.
In primul rand as vrea sa creez un nou control, un User Control care sa fie bila ce trebuie miscata in interioru cadrului. Pentru a realiza (desena) bila, ceea ce voi face este sa suprascriu metoda OnPaint a controlului.
O imagine uneori spune mai mult decat 1000 de cuvinte, asa ca nu o saxplic in prea multe cuvinte ceea ce voi face, ci voi posta diverse capturi de ecran care sa arate ceea ce fac.
Pentru a realiza ceea ce mi-am propus vi urma cativa pasi:
1. Creez un nou User Control:
Am numit acest control… Ball. In fereastra controlului am apasat F7 pentru a deschide codul controlului, loc in care am introduc 2 proprietati (deplasarea pe cele doua axe): XDir si YDir, care reprezinta viteza de deplasare pe directia X, respectiv Y. Deasemeni am si modificat metoda OnPaint pentru a desena bila. In final codul arata asa:
using System; using System.Drawing; using System.Windows.Forms; using System.Drawing.Drawing2D; namespace zeltera.BasicAnimation { public partial class Ball : UserControl { int xDir = 2; int yDir = 1; public Ball() { InitializeComponent(); SetStyle(ControlStyles.UserPaint, true); SetStyle(ControlStyles.AllPaintingInWmPaint, true); SetStyle(ControlStyles.ContainerControl, true); SetStyle(ControlStyles.SupportsTransparentBackColor, true); } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); Pen p = new Pen(Color.Red, 2f); Brush b = new LinearGradientBrush(this.ClientRectangle, Color.Red, Color.Beige, LinearGradientMode.ForwardDiagonal); using (Graphics g = e.Graphics) { int w = this.ClientRectangle.Width - 2 * (int)Math.Ceiling(p.Width); int h = this.ClientRectangle.Height - 2 * (int)Math.Ceiling(p.Width); Rectangle r = new Rectangle(1, 1, w, h); g.SmoothingMode = SmoothingMode.AntiAlias; g.DrawEllipse(p, r); //deseneaza conturul bilei g.FillEllipse(b, r); //deseneaza interiorul bilei GraphicsPath gp = new GraphicsPath(); gp.AddEllipse(0, 0, w, h); this.Region = new Region(gp); } } public int XDir { get { return xDir; } set { xDir = value; } } public int YDir { get { return yDir; } set { yDir = value; } } } }
Acum bila arata in forma finala – nu vreau in etapa aceasta sa lucrez mai mult la definirea detaliilor.
Acum revin la fereastra aplicatiei, unde va trebui sa adaog un obiect de tip Panel, care va constitui locul de miscare al bilei, iar in interiorul lui o bia.
2. Crearea cadrului unde se mica bila:
In fereastra principala am creat 2 obiecte:
private System.Windows.Forms.Panel pnlFrame; private System.Windows.Forms.Timer timer1;
un cadru unde se mica bila si un timer, care va avea rolul de a redesena bila in noua pozitie la intervale de timp definite de mine (am setat proprietatea Interval a timerului la 10 si i-am agatat un EventHandler pentru evenimentul Tick – timer1_Tick).
3. Acum adaog o bila in interiorul Panel-ului:
Ball b1 = new Ball(); balls.Add(b1); pnlFrame.Controls.AddRange(balls.ToArray()); b1.Size = new Size(25, 25);
4. In eventHandler-ul obiectului timer, la fiecare tick, redesenez bila intr-o pozitie noua (incrementez coordonatele x si y ale bilei):
private void timer1_Tick(object sender, EventArgs e) { for (int i = 0; i < pnlFrame.Controls.Count; i++) { Ball b = pnlFrame.Controls[i] as Ball; if(b!=null) MoveBall(b); } }
A se remarca faptul ca acest handler este facut pentru a muta un numar nedefinit de bile. El muta toate bilele pe care le gaseste in pnlFrame.Controls.
In acest moment aplicatia arata cam asa (o bila care se plimba intr-un dreptughi):
.
In final am decis sa afisez 3 bile. Codul complet care face acest lucru este:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace zeltera.BasicAnimation { public partial class frmFrame : Form { private List<Ball> balls; public frmFrame() { InitializeComponent(); balls = new List<Ball>(); CreateBalls(); timer1.Start(); } private void CreateBalls() { Ball b1 = new Ball(); Ball b2 = new Ball(); Ball b3 = new Ball(); balls.AddRange(new Ball[] { b1, b2, b3 }); pnlFrame.Controls.AddRange(balls.ToArray()); b1.Size = new Size(25, 25); b2.Location = new Point(50, 50); b2.XDir = 4; b2.Size = new Size(25, 25); b3.Location = new Point(130, 130); b3.XDir = 5; b3.YDir = 4; b3.Size = new Size(25, 25); } private void timer1_Tick(object sender, EventArgs e) { for (int i = 0; i < pnlFrame.Controls.Count; i++) { Ball b = pnlFrame.Controls[i] as Ball; if (b != null) MoveBall(b); } } private void MoveBall(Ball b) { int x = b.Location.X; int y = b.Location.Y; //check colision with margins if (x + b.XDir > pnlFrame.Size.Width - b.Width || x + b.XDir < 0) b.XDir *= (-1); if (y + b.YDir > pnlFrame.Size.Height - b.Height || y + b.YDir < 0) b.YDir *= (-1); Point p = new Point(x + b.XDir, y + b.YDir); b.Location = p; } private Point GetBallAbsoluteCenter(Ball b) { int x = b.Location.X + (int)b.Size.Width / 2; int y = b.Location.Y + (int)b.Size.Height / 2; return new Point(x, y); } private double GetDistance(Point p1, Point p2) { return Math.Sqrt(Math.Abs((p1.Y - p2.Y) * (p1.Y - p2.Y) + (p1.X - p2.X) * (p1.X - p2.X))); } } }
.
Ce ii lipseste acestei aplicatii: nu se intampla nimic atunci cand doua bile se ciocnesc, trec una prin alta fara a interactiona. Asta ar trebui rezolvat in etapa urmatoare, insa se pare ca nu e o operatie simpla (si deocamdata nu am habar cum se face) asa ca o sa implementez asta mai tarziu – un articol viitor.
Codul complet al apicatiei poate fi descarcat de aici!
Pingback/Trackback
Controale in .Net CE – Partea 3-a | by zeltera