Making a object bounce - c#

I am trying to make a elipse bounce on a rectangle. I am using a timer to move the elipse in both x and y-direction. My idea was to create an if statement to see if the coordinates of the elipse matches the coordinates of the rectangle.
Here is the code I had written so far:
public partial class Form1 : Form
{
Class1 square = new Class1();
public int before;
public int after;
public int c;
public Form1()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs e)
{
after = 590 - (b * b) / 100;
before = 100 + (a * a) / 100;
c = a + b;
Graphics g = e.Graphics;
SolidBrush Brush = new SolidBrush(Color.White);
g.FillEllipse(Brush, a, before, 10, 10);
square.Draw(g);
if (k >= square.y && a >= square.x && a <= square.x + 40)
{
a=c;
before= after;
timer1.Start();
timer2.Stop();
}
else if (k >= square.y + 10)
{
timer2.Stop();
MessageBox.Show("You lost");
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
square.x = e.X;
Cursor.Hide();
}
public int a = 0;
public int b = 0;
public void timer2_Tick(object sender, EventArgs e)
{
a += 1;
Invalidate();
}
private void timer1_Tick(object sender, EventArgs e)
{
b += 1;
Invalidate();
}
}
I know that there are problems. And I have some questions:
Is there a easier way to make the elipse " bounce"?
Is the problem solely with the maths of the curve that the elipse is following?
I know the question may be somewhat undefined or abstract but any help is appriciated. And if you want me to be clearer in some ways, let me know! Thanks

A simple way to make the ellipse bounce of the edges is to check its edge points against the bounds, and then just invert the proper direction. So, something like this should work, if you'll pardon the pseudo-code
loop used to animate the ellipse
before moving, check the position:
if right-most position == right wall
invert x velocity
if left-most position == left wall
invert x velocity
if top-most position == top wall
invert y velocity
if bottom-most position == bottom wall
invert y velocity
move ellipse to next position
This is a pretty simple simulation, but it should give you an idea of how to progress and develop a more sophisticated model. Hope this helps!

Related

Detect mouse movement if Cursor is locked

I am developing Cad application and want to implement snap - when user moves mouse near some object, I set Cursor.Position to the center point of that object. If user moves mouse say 7 pixels in any direction, then Cursor is set free.The way I do it is - I store snaped Cursor position and then under MouseMoveEvent I calculate the distance from stored position to current position. If this position is smaller than defined threshold then I set current cursor position back to stored value. Every time MouseMoveEvent is called that small difference between two cursor positions is added to previously calclated difference, so sooner or later my threshold is reached and Cursor jumps out of snaped position. Code sample:
var x = Cursor.Position.X - storedPosition.X;
pixelsX += x;
int threshold = 7;
if (pixelsX > threshold)
{
Cursor.Position = new System.Drawing.Point(storedPosition.X + 10, storedPosition.Y);
snapReleased = true;
}
The problem with this is that in every MouseMoveEvent mouse is moved very small amount and if threshold is not reached it is set back to stored position which makes Cursor blink(which is very annoying) So my question is - is there a way to detect mouse movement if Cursor is locked in one position?
I would not "snap" the mouse pointer. Do you know the feeling when your mouse is stuck? Depending on your age you may remember roller mice, those with a rubber ball inside. It is horrible.
Instead, I think that the objects you are about to select or are currently moving should react with a snap. For example, when you are about to select an object, when the mouse pointer is closer than the threshold the object gets highlighted. The user can the click the mouse and grab the object.
When moving an object, the object can snap into place when closer than the threshold to another object, guide lines, etc.
The following is a custom panel control that demonstrates an owner drawn cursor that snaps to a grid (snapPoints). The system cursor is hidden/shown on mouse entry/leave. The snap to point distance is controlled by the constant snapLimit.
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Drawing;
namespace WindowsFormsApplication1
{
public class DrawingSurface : Panel
{
private const double snapLimit = 7.0D;
private List<Point> snapPoints = new List<Point>();
private Point cursorPos;
private Point lastDrawnPos;
private bool drawCursor;
public DrawingSurface() : base()
{
this.BorderStyle = BorderStyle.Fixed3D;
this.BackColor = Color.AliceBlue;
this.DoubleBuffered = true;
this.Cursor = Cursors.Cross;
}
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
System.Windows.Forms.Cursor.Hide();
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
System.Windows.Forms.Cursor.Show();
this.drawCursor = false;
this.Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
foreach (Point dot in this.snapPoints)
{
e.Graphics.FillEllipse(Brushes.Red, dot.X - 1, dot.Y - 1, 2, 2);
}
if (drawCursor)
{
Cursor cur = System.Windows.Forms.Cursor.Current;
Point pt = this.cursorPos;
pt.Offset(-cur.HotSpot.X, -cur.HotSpot.Y);
Rectangle target = new Rectangle(pt, cur.Size);
cur.Draw(e.Graphics, target);
this.lastDrawnPos = this.cursorPos;
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
SetCursor(e.Location);
}
private void SetCursor(Point loc)
{
this.cursorPos = loc;
foreach (Point pt in this.snapPoints)
{
double deltaX = loc.X - pt.X;
double deltaY = loc.Y - pt.Y;
double radius = Math.Sqrt((deltaX * deltaX) + (deltaY * deltaY));
if (radius < snapLimit)
{
this.cursorPos = pt;
break;
}
}
if (lastDrawnPos != this.cursorPos)
{
this.drawCursor = true;
this.Invalidate();
}
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
this.snapPoints.Clear();
for (int y = 0; y <= this.ClientRectangle.Height; y += 50)
{
for (int x = 0; x <= this.ClientRectangle.Width; x += 50)
{
this.snapPoints.Add(new Point(x, y));
}
}
}
}
}

winforms c#. I draw the line in code, how can I add event hover to it? [duplicate]

My program can draw lines using canvas.Drawline(). How to click line and change this color (select line)?
private List<Point> coordFirst = new List<Point>();
private List<Point> coordLast = new List<Point>();
public Graphics canvas;
private void Form1_Load(object sender, EventArgs e)
{
canvas=panel1.CreateGraphics();
}
Coordinate line stored in coordFirs & coodLast.
Here is a suitable Line class:
class Line
{
public Color LineColor { get; set; }
public float Linewidth { get; set; }
public bool Selected { get; set; }
public Point Start { get; set; }
public Point End { get; set; }
public Line(Color c, float w, Point s, Point e)
{ LineColor = c; Linewidth = w; Start = s; End = e; }
public void Draw(Graphics G)
{ using (Pen pen = new Pen(LineColor, Linewidth)) G.DrawLine(pen, Start, End); }
public bool HitTest(Point Pt)
{
// test if we fall outside of the bounding box:
if ((Pt.X < Start.X && Pt.X < End.X) || (Pt.X > Start.X && Pt.X > End.X) ||
(Pt.Y < Start.Y && Pt.Y < End.Y) || (Pt.Y > Start.Y && Pt.Y > End.Y))
return false;
// now we calculate the distance:
float dy = End.Y - Start.Y;
float dx = End.X - Start.X;
float Z = dy * Pt.X - dx * Pt.Y + Start.Y * End.X - Start.X * End.Y;
float N = dy * dy + dx * dx;
float dist = (float)( Math.Abs(Z) / Math.Sqrt(N));
// done:
return dist < Linewidth / 2f;
}
}
Define a List for the lines, probably at class level:
List<Line> lines = new List<Line>();
Here is how you can initialize it with a few lines:
for (int i = 0; i < 20; i++) lines.Add(new Line(Color.Black, 4f,
new Point(R.Next(panel1.Width), R.Next(panel1.Height)),
new Point(R.Next(panel1.Width), R.Next(panel1.Height))));
Here is the result of clicking on a crossing:
Whenever you add, change or remove a line you need to make the Panel reflect the news by triggering the Paint event:
panel1.Invalidate();
Here is the Paint event of the Panel:
private void panel1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
foreach (Line L in lines) L.Draw(e.Graphics);
}
In the MouseClick event you do the test:
private void panel1_MouseClick(object sender, MouseEventArgs e)
{
foreach(Line L in lines)
L.LineColor = L.HitTest(e.Location) ? Color.Red : Color.Black;
panel1.Invalidate();
}
To avoid flicker don't use the basic Panel class as it isn't doublebuffered. Instead use either a PictureBox or a Label (with AutoSize=false) or a doublebuffered Panel subclass:
class DrawPanel : Panel
{ public DrawPanel () { DoubleBuffered = true; } }
Notes:
There is no such thing as a 'Line' in WinForms, only pixels of various colors. So to select a line you need to store it's two endpoints' coordinates and then find out if you have hit it when clicking.
The above example shows how to do it in math.
Instead one could test each line by drawing it onto a bitmap and test the pixel the mouse has clicked. But drawing those bitmaps would have to do math behind the scenes as well and also allocate space for the bitmaps, so the math will be more efficient..
Yes the Line class looks a little long for such a simple thing a s a line but look how short all the event codes now are! That's because the responsiblities are where they belong!
Also note the the first rule of doing any drawing in WinForms is: Never cache or store a Grahics object. In fact you shouldn't ever use CreateGraphics in the first place, as the Graphics object will never stay in scope and the graphics it produces will not persist (i.e. survive a Minimize-maximize sequence)..
Also note how I pass out the e.Graphics object of the Paint event's parameters to the Line instances so they can draw themselves with a current Graphics object!
To select thinner lines it may help to modify the distance check a little..
The Math was taken directly form Wikipedia.
You can change the color of everything on click. By using click event of particular object.
I give you an example for button. If you click on button then panal’s color will be change. You can modify the code as per your requirement.
private List<Point> coordFirst = new List<Point>();
private List<Point> coordLast = new List<Point>();
public Graphics canvas;
private void Form1_Load(object sender, EventArgs e)
{
canvas = panel1.CreateGraphics();
}
private void panel1_Click(object sender, EventArgs e)
{
panel1.BackColor = Color.Blue;
}
private void nonSelectableButton3_Click(object sender, EventArgs e)
{
panel1.BackColor = Color.BurlyWood;
}

WinForm Triangle animation

I am trying to make a triangle move back and forth over an arc, the triangle shoud rotate while moving.
I have made a picture to explain it better:
https://app.box.com/s/mt9p66zlmtkkgkdvtb5h
The math looks right to me, can anyone tell me what I am doing wrong?
public partial class Form1 : Form
{
bool turn = false;
double angle = 0;
public Form1()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Brush solidBlackBrush = new SolidBrush(Color.Black); //En solid svart brush som brukes flere steder
Pen solidBackPen = new Pen(solidBlackBrush);//En solid svart pen som brukes flere steder
//Trekant = Norwegian for Triangle, Trekant is a class that draws a polygon shaped as a Triangle.
Trekant tre = new Trekant();
e.Graphics.DrawArc(solidBackPen, new Rectangle(new Point(50,50), new Size(100,100)) , 180, 180);
//X = a + r*Cos(angle) | Y = b + r*Sin(angle)
double x = (50+(100/2)) + (100/2) * Math.Cos(Trekant.DegreeToRadian(angle));
double y = (50+(100/2)) - (100/2) * Math.Sin(Trekant.DegreeToRadian(angle));
e.Graphics.TranslateTransform((float)x - 15, (float)y - 40);//Flytter 0 slik at pistolen havner på rett sted
e.Graphics.RotateTransform((float)-Trekant.RadianToDegree(Trekant.DegreeToRadian(angle-90)));
tre.Draw(e.Graphics);
}
private void timer1_Tick(object sender, EventArgs e)
{
if (angle == 0)
{
turn = false;
}
if (angle == 180)
{
turn = true;
}
if (turn)
{
angle -= 10;
}
if (!turn)
{
angle += 10;
}
this.Invalidate();
}
}
Without going into coding let's first set up the math..
Let say the half ellipse in the picture has a width of 2w and a height of h. And lets assume you want the movement to happen in n steps.
Then at each step s the rotation angle is s * 180f/n. The rotation point's x stays at w plus whatever offset ox the ellipse has, but will have to move its y vertically from offset oy, first by (w-h) * 2f / n down on each step and then up again by the same amounts..
The Drawing itself moves accordingly.
So you have a TranslateTransform for the rotation point, the RotateTransform, then another TranslateTransform to place the image, then the DrawImage and finally a ResetTransform.
I hope that helps. If that doesn't work, please update the question and we'll can get it right, I'm sure..

How can i make on the pixels fade in/out like effect?

Today im doing the pixels in the paint event to blink.
In form1 i have this code in a timer tick event that the interval is set to 1000ms.
private void timer1_Tick(object sender, EventArgs e)
{
CloudEnteringAlert.cloudColorIndex = (CloudEnteringAlert.cloudColorIndex + 1) % CloudEnteringAlert.cloudColors.Length;
pictureBox1.Invalidate();
}
In the CloudEntering class i have on the top:
public static Brush[] cloudColors = new[] { Brushes.Yellow, Brushes.Transparent };
Then in a paint method this paint method im calling from the form1 pictureBox1 paint event:
foreach (PointF pt in clouds)
{
e.FillEllipse(cloudColors[cloudColorIndex], pt.X * (float)currentFactor, pt.Y * (float)currentFactor, 7f, 7f);
}
So what i see now is one second the pixels are in yellow and one second the pixels are in Transparent.
Now what i want to do is:
Each pixel will start from radius 5.
Each pixel radius will get bigger to 25.
The animation will be from the top to the bottom.
Each pixel that got to radius 25 will start to get smaller back to radius 5.
When the first pixel is started from 5 will get to radius 15 the next one will start to get bigger.
So if the first pixel is start at 5 now its 15 the next one will start to get bigger and the first one will continue to 25 when the first one is 25 it will get smaller. The second one when its 15 the third one will get bigger and so on.
All the pixels will start at radius 5 but only the first one will start get bigger get to 15 then the next one and when the first is 25 it will get smaller .
This is the time between each pixel.
And each pixel radius size change should take 300ms !
How can i do it ?
You want to start by encapsulating all information needed to render a single ellipse into a separate class. That would be something like:
public class Ellipse
{
public PointF Center { get; set; }
public Brush Brush { get; set; }
public float Diameter { get; set; }
public float DiameterDelta { get; set; }
public Ellipse(float x, float y)
{
Center = new PointF(x, y);
Brush = Brushes.Blue;
Diameter = 5;
DiameterDelta = 1;
}
}
The Ellipse.DiameterDelta property is the delta value which will be used for animation, and it can be positive (when going from diameter 5 to diameter 25), or negative (when going backwards). The value of this property (I've used 1 above) together with the your Timer.Interval will influence the speed of your animation.
A better OOP design would probably advocate moving animation-related properties out of this class, but for simplicity sake, it's better to start with this.
In your timer event, you might have something like:
private void timer_Tick(object sender, EventArgs e)
{
// presuming that you made a separate user control
// which has a collection of ellipses in a `Clouds` property
foreach (var c in cloudBox.Clouds)
Animate(c);
cloudBox.Invalidate();
}
private void Animate(Ellipse c)
{
// update diameter
c.Diameter += c.DiameterDelta;
// when you reach bounds, change delta direction
if ((c.DiameterDelta < 0 && c.Diameter <= 5) ||
(c.DiameterDelta > 0 && c.Diameter >= 25))
c.DiameterDelta = -c.DiameterDelta;
}
To get a smooth, non flickering animation, you will most likely have to use a custom control with ControlStyles.AllPaintingInWmPaint and ControlStyles.OptimizedDoubleBuffer set in its constructor, so instead of a PictureBox, I would do something like:
public partial class CloudBox : UserControl
{
public CloudBox()
{
InitializeComponent();
SetStyle(
ControlStyles.AllPaintingInWmPaint |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.UserPaint |
ControlStyles.ResizeRedraw, true);
}
private readonly List<Ellipse> _clouds = new List<Ellipse>();
public List<Ellipse> Clouds
{
get { return _clouds; }
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
foreach (var cloud in _clouds)
{
e.Graphics.FillEllipse(
cloud.Brush, cloud.Center.X, cloud.Center.Y,
cloud.Diameter, cloud.Diameter);
}
base.OnPaint(e);
}
}
I haven't tested this, but I believe you'll be able to fill in the details. Setting Timer.Interval to 10 or 20 ms should yield a pretty fluid animation IMHO.
[Edit] To instantiate the whole thing with some test data (I don't know where you get it from), you could use something like this in your Form.Load event handler:
for (int i = 0; i < 400; i += 50)
for (int j = 0; j < 400; j += 50)
cloudBox.Clouds.Add(new Ellipse(i, j));

Is there a way to avoid writing duplicate code in C#?

I would like to insert more balls into the form. However, in order to allow it to bounce I have to code the same algorithm over and over again for different ball. May I know is there any way I can do it without writing it over and over again? The codes I have are as below.
int bBA1; //The x axis from the upper left corner
int bBA2; //The y axis from the upper left corner
int spdBBA1; //The change of x
int spdBBA2; //The change of y
public StartGame()
{
InitializeComponent();
}
private void StartGame_Load(object sender, EventArgs e)
{
//Loads the ball on the screen at bottom of the window
bBA1 = this.ClientSize.Width / 5; //The x axis the ball is loaded at
bBA2 = this.ClientSize.Height - 10; //The y axis the ball is loaded at
spdBBA1 = 1; //The speed of the ball of y
spdBBA2 = 1; //The speed of the ball of x
}
private void StartGame_Paint_1(object sender, PaintEventArgs e)
{
//This is the inner paint color of the circle that is 10 by 10
e.Graphics.FillEllipse(Brushes.Blue, bBA1, bBA2, 10, 10);
//This is the outline paint color of the circle that is 10 by 10
e.Graphics.DrawEllipse(Pens.Blue, bBA1, bBA2, 10, 10);
}
private void timer1_Tick(object sender, EventArgs e)
{
bBA2 = bBA2 + spdBBA2;
bBA1 = bBA1 + spdBBA1;
if (bBA2 < 0)
{
spdBBA2 = -spdBBA2; //If y is less than 0 then it changes direction
}
else if (bBA1 < -5)
{
spdBBA1 = -spdBBA1;
}
else if (bBA2 + 10 > this.ClientSize.Height)
{
// If y + 10, the radius of the circle is greater than the
// form width then we change direction
spdBBA2 = -spdBBA2;
}
else if (bBA1 + 10 > this.ClientSize.Width)
{
spdBBA1 = -spdBBA1;
}
this.Invalidate();
}
Thank you.
Yes you can! This is one of the many cool features of object oriented programming.
Create a Ball class. When you start the game create all the balls you need and store them in a list. From there you can use foreach loops to modify the properties of each of your Ball objects.
public class Ball
{
public int speedX { get; private set; }
public int speedY { get; private set; }
public int positionX { get; private set; }
public int positionY { get; private set; }
public Ball(int speedX, int speedY, int positionX, int positionY)
{
this.speedX = speedX;
this.speedY = speedY;
this.positionX = positionX;
this.positionY = positionY;
}
public int setSpeedX(int newSpeed)
{
this.speedX = newSpeed;
}
//Add any other setters you need.
}
Now you have a blueprint for any balls you need to create. Then in your game you can do something like this:
public class StartGame
{
public List<Ball> ballList { get; private set; }
public StartGame()
{
this.ballList = new List<Ball>();
InitializeComponent();
}
private void StartGame_Load(object sender, EventArgs e)
{
//Add any balls you need here.
ballList.add(new Ball(5, 10, 1, 1));
ballList.add(new Ball(2, 17, 2, 9));
ballList.add(new Ball(4, 12, 7, 5));
}
private void StartGame_Paint_1(object sender, PaintEventArgs e)
{
//This foreach loop will run through all the balls in ballList
foreach(Ball ball in ballList)
{
e.Graphics.FillEllipse(Brushes.Blue, ball.positionX, ball.positionY, 10, 10);
e.Graphics.DrawEllipse(Pens.Blue, ball.positionX, ball.positionY, 10, 10);
}
}
}
I wasn't 100% sure how your game worked so I made some guesses with the variables but I hope you get the idea.
I would look at using a foreach and the list to store them all
Start by creating a ball class and, on start-up, create all the balls you want (at least that you need for the time being) and save that to the list, then just use a foreach loop to change anything you want with the ball(s).

Categories