I have a program that prints the cube and which can be rotated. Here's the code.
public partial class ProjectorForm : Form
{
Projector projector;
Cube cube;
float deltaRot;
public ProjectorForm()
{
InitializeComponent();
}
private void ProjectorForm_Load(object sender, EventArgs e)
{
deltaRot = 0.01f;
projector = new Projector();
cube = new Cube(Vector3.UnitZ * 20*10, 10*10, 10*10, 15*10);
updateTimer.Start();
}
private void updateTimer_Tick(object sender, EventArgs e)
{
if (rotXBox.Checked)
cube.RotateX(deltaRot);
if (rotYBox.Checked)
cube.RotateY(deltaRot);
if (rotZBox.Checked)
cube.RotateZ(deltaRot);
doubleBufferedPanel1.Invalidate();
}
private void doubleBufferedPanel1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(Color.White);
cube.Draw(projector, Color.Black, doubleBufferedPanel1.ClientSize.Width, doubleBufferedPanel1.ClientSize.Height, e.Graphics);
}
private void button1_Click(object sender, EventArgs e)
{
deltaRot = float.Parse(deltaRotBox.Text);
}
}
class Projector
{
public Vector3 cameraPosition;
public float planeDistance;
ProjectorForm n = new ProjectorForm();
public Projector()
{
cameraPosition = Vector3.Zero;
planeDistance = 256; //Here, multiply by 2 and the scaled cube, how to make that scale when you press the button.
}
public PointF Project(Vector3 point, float width, float height)
{
float x = cameraPosition.X + ((cameraPosition.Z + planeDistance) / (point.Z - cameraPosition.Z)) * (point.X - cameraPosition.X) + width / 2;
float y = cameraPosition.Y + ((cameraPosition.Z + planeDistance) / (point.Z - cameraPosition.Z)) * (point.Y - cameraPosition.Y) + height / 2;
return new PointF(x, y);
}
public void DrawLine(Color color, Vector3 p1, Vector3 p2, float width, float height, Graphics g)
{
g.DrawLine(new Pen(color), Project(p1, width, height), Project(p2, width, height));
}
public void FillPolygon(Color color, Vector3[] vertices, float width, float height, Graphics g)
{
PointF[] points = new PointF[vertices.Length];
for (int i = 0; i < points.Length; i++)
points[i] = Project(vertices[i], width, height);
g.FillPolygon(new SolidBrush(color), points);
}
}
How to make a cube can be scaled by pressing a button. I found the variable planeDistance in the class Projector, when it increased by 2 times the cube is scaled, but I do not know how it can be increased by means of a button.
The field planeDistance is public, so you can change it from outside the class. i.e. just add something like the following to the event handler of a button:
projector.planeDistance += 10; // Change 10 as appropriate
It's worth noting that this doesn't change the size of the cube, it changes how far away the camera is from it. So, while the cube appears to be changing in size, that's just because the camera is moving closer / further away.
To actually change the size of the cube you would have to change fields in the cube class.
Since the size is defined by the vectors created in the constructor you don't really have an easy way of changing them once the cube is created.
You could create a new cube whenever you want to change the size (keep track of the size in another variable on the form).
You could add a method to the cube class that creates new vectors that define the new size (it would look a bit like the constructor, only populating the arrays, not creating them).
You could add a size field to your cube, always create a unit cube (1, 1, 1) then when rendering multiply each vector by your size.
Related
I am new to C# and I am trying to use OOP and classes. I am trying to draw a simple sinewave and an axis line (X axis). I have gotten similar code to in the "main - Form1" but I cannot get it to draw in the form from within a class. It draws nothing! The code does compile.
What am I missing? What can I do better?
I call the class from a button click -
Drawclick(object sender, EventArgs e)
{ DrawSine Sine1 = new DrawSine(950);
}
Here is the class
class DrawSine:Form1
{
private float sinex=0;//set up variables for sine
private float countx = 0;
private float siney = 0;
private float sinex1=0;
private float siney1=0;
public float offset;
public float scalex;
public float scaley;
public DrawSine(int widthG)
{
Graphics Graphsine = this.CreateGraphics();//declare sine
Graphics Graphline = this.CreateGraphics();//declare axis
Pen Graphpen = new Pen(Brushes.Black, 1.0F);
Graphline.DrawLine(Graphpen, 0, 200, widthG, 200);//draw line
0,200 to end of form,200
int WidthG = widthG;
do //draw sine wave left to right
{
sinex += scalex * 6.28f / widthG;
siney = (float)Math.Sin(sinex);
Graphsine.DrawLine(Graphpen, sinex1, siney1 + offset,
countx, siney * 100 + offset);
sinex1 = sinex;
sinex1 = siney * 100;
countx += 1;
}
while (countx <= widthG);
}
}
This may be drawing something on the form but you cannot see because soon after the Load event, there will be a PaintBackground event and a PaintEvent as the form is rendered.
These events will effectively erase everything on the form.
2 Remedies:
Encapsulate the above draw code in a method (Let's say DrawSine())
One, Override OnPaint event and write your code there. Your painting will remain intact on form resize or form redraw.
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
DrawSine(e, 950);
}
Two, use form Shown event
private void Form1_Shown(object sender, EventArgs e)
{
DrawSine(null, 950);
}
Your original method:
public DrawSine(Graphics g, int widthG)
{
Graphics Graphsine = g??this.CreateGraphics();//declare sine
Graphics Graphline = g??this.CreateGraphics();//declare axis
Pen Graphpen = new Pen(Brushes.Black, 1.0F);
Graphline.DrawLine(Graphpen, 0, 200, widthG, 200);//draw line
//0,200 to end of form,200
int WidthG = widthG;
do //draw sine wave left to right
{
sinex += scalex * 6.28f / widthG;
siney = (float)Math.Sin(sinex);
Graphsine.DrawLine(Graphpen, sinex1, siney1 + offset,
countx, siney * 100 + offset);
sinex1 = sinex;
sinex1 = siney * 100;
countx += 1;
}
while (countx <= widthG);
}
I am working on a project for school, we need to make a basic top down race game in C# without using XNA.
First of all let me tell you that the stuff we have learned about programming so far has little to do with making something that even remotely looks like a racegame. It didn't get any more difficult than array's, loops etc.
So we didn't learn about graphics or anything like that.
Having said all that I am having the following problem.
We have created a Graphics object, and then use DrawImage and use a bitmap from a car.jpg.
graphics = e.Graphics;
graphics.RotateTransform(angle);
graphics.DrawImage(car, xPos, yPos, car.Width, car.Height);
Then we wait for a key press e.g Right
case Keys.Right:
if (angle != 360)
{
angle += 10;
}
else
{
angle = 0;
}
this.Refresh();
break;
The problem we have is that the pivot point for the rotation is in the top left corner. So as soon as we move the car to something like (20,25) and start to rotate it, it will use (0,0) as the center of rotation. What we want to achieve is to have the center point of rotation at the center of our car.
We have tried looking for ways to change the centerX and centerY of the RotateTransform but have come to the conclusion that this isn't possible with the bitmap.
We have been struggling with this problem for over 2 days and can't seem to find any solution for achieving the thing we want.
Is there something we are doing wrong creating the Graphics object, or is there a totally different way to change centerX and centerY for the car?
To draw a rotated Bitmap you need to do a few steps to prepare the Graphics object:
first you move its origin onto the midpoint of the rotation
then you rotate by the desired angle
next you move it back
now you can draw the Bitmap
finally you reset the Graphics
This needs to be done for each bitmap.
Here are the steps in code to draw a Bitmap bmp at position (xPos, yPos):
float moveX = bmp.Width / 2f + xPos;
float moveY = bmp.Height / 2f+ xPosf;
e.Graphics.TranslateTransform(moveX , moveY );
e.Graphics.RotateTransform(angle);
e.Graphics.TranslateTransform(-moveX , -moveY );
e.Graphics.DrawImage(bmp, xPos, yPos);
e.Graphics.ResetTransform();
There is one possible complication: If your Bitmap has different dpi resolution than the screen i.e. than the Graphics you must first adapt the Bitmap's dpi setting!
To adapt the Bitmapto the usual 96dpi you can simply do a
bmp.SetResolution(96,96);
To be prepared for future retina-like displays you can create a class variable you set at startup:
int ScreenDpi = 96;
private void Form1_Load(object sender, EventArgs e)
{
using (Graphics G = this.CreateGraphics()) ScreenDpi = (int)G.DpiX;
}
and use it after loading the Bitmap:
bmp.SetResolution(ScreenDpi , ScreenDpi );
As usual the DrawImage method uses the top left corner of the Bitmap. You may need to use different Points for the rotation point and possibly also for the virtual position of your car, maybe in the middle of its front..
Here is static class which will paint the image in desired location within desired area. Change the rotationangle value to rotate the image. And you can also pan and zoom the image.
Add this class in your Project and call the static functions from Win Form.
public static class FullImage
{
public static Image image;
public static RectangleF DisplayRect, SourceRect;
public static Size ParentBoundry;
public static float rotationangle=0;
internal static void Paint(Graphics graphics)
{
if (image == null)
return;
float hw = DisplayRect.X + DisplayRect.Width / 2f;
float hh = DisplayRect.Y + DisplayRect.Height / 2f;
System.Drawing.Drawing2D.Matrix m = graphics.Transform;
m.RotateAt(rotationangle, new PointF(hw, hh), System.Drawing.Drawing2D.MatrixOrder.Append);
graphics.Transform = m;
graphics.DrawImage(image, new RectangleF(DisplayRect.X, DisplayRect.Y, DisplayRect.Width, DisplayRect.Height), SourceRect, GraphicsUnit.Pixel);
graphics.ResetTransform();
}
public static void LoadImage(Image img)
{
image = img;
SizeF s = GetResizedSize(image, ParentBoundry);
SourceRect = new RectangleF(0, 0, image.Width, image.Height);
DisplayRect = new RectangleF(ParentBoundry.Width / 2 - s.Width / 2, ParentBoundry.Height / 2 - s.Height / 2, s.Width, s.Height);
}
public static Size GetResizedSize(Image ImageToResize, Size size)
{
int sourceWidth = ImageToResize.Width;
int sourceHeight = ImageToResize.Height;
float nPercent = 0;
float nPercentW = 0;
float nPercentH = 0;
nPercentW = ((float)size.Width / (float)sourceWidth);
nPercentH = ((float)size.Height / (float)sourceHeight);
if (nPercentH < nPercentW)
nPercent = nPercentH;
else
nPercent = nPercentW;
int destWidth = (int)(sourceWidth * nPercent);
int destHeight = (int)(sourceHeight * nPercent);
return new Size(destWidth, destHeight);
}
internal static void MouseWheel(int delta)
{
if (delta > 0)
DisplayRect = ZoomImage(DisplayRect,CurrentMouse, .1f);
else
DisplayRect = ZoomImage(DisplayRect, CurrentMouse, -.1f);
}
private RectangleF ZoomImage(RectangleF ImageRectangle, PointF MouseLocation, float ScaleFactor)
{
/// Original Size and Location
SizeF OriginalSize = ImageRectangle.Size;
PointF OriginalPoint = ImageRectangle.Location;
///Mouse cursor location -located in width% and height% of totaloriginal image
float mouse_widthpercent = System.Math.Abs(OriginalPoint.X - MouseLocation.X) / OriginalSize.Width * 100;
float mouse_heightpercent = System.Math.Abs(OriginalPoint.Y - MouseLocation.Y) / OriginalSize.Height * 100;
///Zoomed Image by scalefactor
SizeF FinalSize = new SizeF(OriginalSize.Width + OriginalSize.Width * ScaleFactor, OriginalSize.Height + OriginalSize.Height * ScaleFactor);
if (FinalSize.Width < 15 || FinalSize.Height < 15)
return ImageRectangle;
if (FinalSize.Width > 60000 || FinalSize.Height > 60000)
return ImageRectangle;
/// How much width increases and height increases
float widhtincrease = FinalSize.Width - OriginalSize.Width;
float heightincrease = FinalSize.Height - OriginalSize.Height;
/// Adjusting Image location after zooming the image
PointF FinalLocation = new System.Drawing.PointF(OriginalPoint.X - widhtincrease * mouse_widthpercent / 100,
OriginalPoint.Y - heightincrease * mouse_heightpercent / 100);
ImageRectangle = new RectangleF(FinalLocation.X, FinalLocation.Y, FinalSize.Width, FinalSize.Height);
return ImageRectangle;
}
static bool drag = false;
static Point Initial, CurrentMouse;
internal static void MouseMove(Point location)
{
CurrentMouse = location;
if (drag)
{
DisplayRect = new RectangleF(DisplayRect.X + location.X - Initial.X, DisplayRect.Y + location.Y - Initial.Y, DisplayRect.Width, DisplayRect.Height);
Initial = location;
}
}
internal static void MouseDown(Point location)
{
Initial = location;
drag = true;
}
internal static void MouseUp(Point location)
{
drag = false;
}
}
After Adding this code in your project (Better add in separate cs file), Call the functions from Win Form class (Form1.cs).
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
FullImage.ParentBoundry = new Size(this.Width, this.Height);
// Enter the image path
FullImage.LoadImage(Image.FromFile(#"D:\a.jpg"));
}
//Create a paint event
private void Form1_Paint(object sender, PaintEventArgs e)
{
FullImage.Paint(e.Graphics);
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
Vault.FullImage.MouseDown(e.Location);
this.Invalidate();
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
Vault.FullImage.MouseMove(e.Location);
this.Invalidate();
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
Vault.FullImage.MouseUp(e.Location);
this.Invalidate();
}
protected override void OnMouseWheel(MouseEventArgs e)
{
Vault.FullImage.MouseWheel(e.Delta);
this.Invalidate();
}
Now, if you want to rotate the image, just set the value however you want (with slider, button or add some more functions to detect the mouse movement and then rotate)
Example: add a button and each time the button clicked increase the value by 1.
private void button1_clicked(object sender, EventArgs e)
{
FullImage.rotationangle++;
this.invalidate();
}
To rotate the top left from the center you first need to know the angle of it then adjust it by the angle you want and re-calculate the new top left by the new angle:
var newXPos = (int)(xPos + car.Width / 2.0 + Math.Cos(Math.Atan2(-car.Height / 2, -car.Width / 2)
+ angle / 180.0 * Math.PI) * -car.Width / 2.0);
var newYPos = (int)(yPos + car.Height / 2.0 + Math.Sin(Math.Atan2(-car.Height / 2, -car.Width / 2)
+ angle / 180.0 * Math.PI) * -car.Height / 2.0);
graphics = e.Graphics;
graphics.RotateTransform(angle);
graphics.DrawImage(car, newXPos, newYPos, car.Width, car.Height);
I am working on a project for school, we need to make a basic top down race game in C# without using XNA.
First of all let me tell you that the stuff we have learned about programming so far has little to do with making something that even remotely looks like a racegame. It didn't get any more difficult than array's, loops etc.
So we didn't learn about graphics or anything like that.
Having said all that I am having the following problem.
We have created a Graphics object, and then use DrawImage and use a bitmap from a car.jpg.
graphics = e.Graphics;
graphics.RotateTransform(angle);
graphics.DrawImage(car, xPos, yPos, car.Width, car.Height);
Then we wait for a key press e.g Right
case Keys.Right:
if (angle != 360)
{
angle += 10;
}
else
{
angle = 0;
}
this.Refresh();
break;
The problem we have is that the pivot point for the rotation is in the top left corner. So as soon as we move the car to something like (20,25) and start to rotate it, it will use (0,0) as the center of rotation. What we want to achieve is to have the center point of rotation at the center of our car.
We have tried looking for ways to change the centerX and centerY of the RotateTransform but have come to the conclusion that this isn't possible with the bitmap.
We have been struggling with this problem for over 2 days and can't seem to find any solution for achieving the thing we want.
Is there something we are doing wrong creating the Graphics object, or is there a totally different way to change centerX and centerY for the car?
To draw a rotated Bitmap you need to do a few steps to prepare the Graphics object:
first you move its origin onto the midpoint of the rotation
then you rotate by the desired angle
next you move it back
now you can draw the Bitmap
finally you reset the Graphics
This needs to be done for each bitmap.
Here are the steps in code to draw a Bitmap bmp at position (xPos, yPos):
float moveX = bmp.Width / 2f + xPos;
float moveY = bmp.Height / 2f+ xPosf;
e.Graphics.TranslateTransform(moveX , moveY );
e.Graphics.RotateTransform(angle);
e.Graphics.TranslateTransform(-moveX , -moveY );
e.Graphics.DrawImage(bmp, xPos, yPos);
e.Graphics.ResetTransform();
There is one possible complication: If your Bitmap has different dpi resolution than the screen i.e. than the Graphics you must first adapt the Bitmap's dpi setting!
To adapt the Bitmapto the usual 96dpi you can simply do a
bmp.SetResolution(96,96);
To be prepared for future retina-like displays you can create a class variable you set at startup:
int ScreenDpi = 96;
private void Form1_Load(object sender, EventArgs e)
{
using (Graphics G = this.CreateGraphics()) ScreenDpi = (int)G.DpiX;
}
and use it after loading the Bitmap:
bmp.SetResolution(ScreenDpi , ScreenDpi );
As usual the DrawImage method uses the top left corner of the Bitmap. You may need to use different Points for the rotation point and possibly also for the virtual position of your car, maybe in the middle of its front..
Here is static class which will paint the image in desired location within desired area. Change the rotationangle value to rotate the image. And you can also pan and zoom the image.
Add this class in your Project and call the static functions from Win Form.
public static class FullImage
{
public static Image image;
public static RectangleF DisplayRect, SourceRect;
public static Size ParentBoundry;
public static float rotationangle=0;
internal static void Paint(Graphics graphics)
{
if (image == null)
return;
float hw = DisplayRect.X + DisplayRect.Width / 2f;
float hh = DisplayRect.Y + DisplayRect.Height / 2f;
System.Drawing.Drawing2D.Matrix m = graphics.Transform;
m.RotateAt(rotationangle, new PointF(hw, hh), System.Drawing.Drawing2D.MatrixOrder.Append);
graphics.Transform = m;
graphics.DrawImage(image, new RectangleF(DisplayRect.X, DisplayRect.Y, DisplayRect.Width, DisplayRect.Height), SourceRect, GraphicsUnit.Pixel);
graphics.ResetTransform();
}
public static void LoadImage(Image img)
{
image = img;
SizeF s = GetResizedSize(image, ParentBoundry);
SourceRect = new RectangleF(0, 0, image.Width, image.Height);
DisplayRect = new RectangleF(ParentBoundry.Width / 2 - s.Width / 2, ParentBoundry.Height / 2 - s.Height / 2, s.Width, s.Height);
}
public static Size GetResizedSize(Image ImageToResize, Size size)
{
int sourceWidth = ImageToResize.Width;
int sourceHeight = ImageToResize.Height;
float nPercent = 0;
float nPercentW = 0;
float nPercentH = 0;
nPercentW = ((float)size.Width / (float)sourceWidth);
nPercentH = ((float)size.Height / (float)sourceHeight);
if (nPercentH < nPercentW)
nPercent = nPercentH;
else
nPercent = nPercentW;
int destWidth = (int)(sourceWidth * nPercent);
int destHeight = (int)(sourceHeight * nPercent);
return new Size(destWidth, destHeight);
}
internal static void MouseWheel(int delta)
{
if (delta > 0)
DisplayRect = ZoomImage(DisplayRect,CurrentMouse, .1f);
else
DisplayRect = ZoomImage(DisplayRect, CurrentMouse, -.1f);
}
private RectangleF ZoomImage(RectangleF ImageRectangle, PointF MouseLocation, float ScaleFactor)
{
/// Original Size and Location
SizeF OriginalSize = ImageRectangle.Size;
PointF OriginalPoint = ImageRectangle.Location;
///Mouse cursor location -located in width% and height% of totaloriginal image
float mouse_widthpercent = System.Math.Abs(OriginalPoint.X - MouseLocation.X) / OriginalSize.Width * 100;
float mouse_heightpercent = System.Math.Abs(OriginalPoint.Y - MouseLocation.Y) / OriginalSize.Height * 100;
///Zoomed Image by scalefactor
SizeF FinalSize = new SizeF(OriginalSize.Width + OriginalSize.Width * ScaleFactor, OriginalSize.Height + OriginalSize.Height * ScaleFactor);
if (FinalSize.Width < 15 || FinalSize.Height < 15)
return ImageRectangle;
if (FinalSize.Width > 60000 || FinalSize.Height > 60000)
return ImageRectangle;
/// How much width increases and height increases
float widhtincrease = FinalSize.Width - OriginalSize.Width;
float heightincrease = FinalSize.Height - OriginalSize.Height;
/// Adjusting Image location after zooming the image
PointF FinalLocation = new System.Drawing.PointF(OriginalPoint.X - widhtincrease * mouse_widthpercent / 100,
OriginalPoint.Y - heightincrease * mouse_heightpercent / 100);
ImageRectangle = new RectangleF(FinalLocation.X, FinalLocation.Y, FinalSize.Width, FinalSize.Height);
return ImageRectangle;
}
static bool drag = false;
static Point Initial, CurrentMouse;
internal static void MouseMove(Point location)
{
CurrentMouse = location;
if (drag)
{
DisplayRect = new RectangleF(DisplayRect.X + location.X - Initial.X, DisplayRect.Y + location.Y - Initial.Y, DisplayRect.Width, DisplayRect.Height);
Initial = location;
}
}
internal static void MouseDown(Point location)
{
Initial = location;
drag = true;
}
internal static void MouseUp(Point location)
{
drag = false;
}
}
After Adding this code in your project (Better add in separate cs file), Call the functions from Win Form class (Form1.cs).
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
FullImage.ParentBoundry = new Size(this.Width, this.Height);
// Enter the image path
FullImage.LoadImage(Image.FromFile(#"D:\a.jpg"));
}
//Create a paint event
private void Form1_Paint(object sender, PaintEventArgs e)
{
FullImage.Paint(e.Graphics);
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
Vault.FullImage.MouseDown(e.Location);
this.Invalidate();
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
Vault.FullImage.MouseMove(e.Location);
this.Invalidate();
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
Vault.FullImage.MouseUp(e.Location);
this.Invalidate();
}
protected override void OnMouseWheel(MouseEventArgs e)
{
Vault.FullImage.MouseWheel(e.Delta);
this.Invalidate();
}
Now, if you want to rotate the image, just set the value however you want (with slider, button or add some more functions to detect the mouse movement and then rotate)
Example: add a button and each time the button clicked increase the value by 1.
private void button1_clicked(object sender, EventArgs e)
{
FullImage.rotationangle++;
this.invalidate();
}
To rotate the top left from the center you first need to know the angle of it then adjust it by the angle you want and re-calculate the new top left by the new angle:
var newXPos = (int)(xPos + car.Width / 2.0 + Math.Cos(Math.Atan2(-car.Height / 2, -car.Width / 2)
+ angle / 180.0 * Math.PI) * -car.Width / 2.0);
var newYPos = (int)(yPos + car.Height / 2.0 + Math.Sin(Math.Atan2(-car.Height / 2, -car.Width / 2)
+ angle / 180.0 * Math.PI) * -car.Height / 2.0);
graphics = e.Graphics;
graphics.RotateTransform(angle);
graphics.DrawImage(car, newXPos, newYPos, car.Width, car.Height);
I need to move a rectangle using angles. Actually I want to change the direction of my moving rectangle when it reaches the location I have given in my code in if statement!
I just need the way I can find out how to move my rectangle at 60, 30, 60, 120, 150, 270 degrees!
Suppose that if
circle.Y>=this.Height-80
See this:
I really actually need to change the direction of rectangle movement using angles! so that at certain location reaches I can change the rectangle direction according to angle of my own choice!
such that:
if(circle.Y>=this.Height-80)
move in the direction of 90 degrees
if(circle.X>=this.Width-80)
move in the direction of 60 degree
as you can see in the screen shot!
What I have been trying is:
public partial class Form1 : Form
{
Rectangle circle;
double dx = 2;
double dy = 2;
public Form1()
{
InitializeComponent();
circle = new Rectangle(10, 10, 40, 40);
}
private void Form1_Load(object sender, EventArgs e)
{
this.Refresh();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.FillEllipse(new SolidBrush(Color.Red), circle);
}
private void timer_Tick(object sender, EventArgs e)
{
circle.X += (int)dx;
circle.Y += (int)dy;
if (circle.Y>=this.Height-80)
{
dy = -Math.Acos(0) * dy/dy; //here i want to change the direction of circle at 90 degrees so that it should go up vertically straight with same speed
}
this.Refresh();
}
}
The Problem is that I have been trying changing my conditions to:
dy = -Math.Asin(1) * dy;
dx = Math.Acos(0) * dx ;
but in both cases nothing is happening and the direction remains same!
I just want to move the circle in inverted upward direction at 90 degrees when it reach at
circle.Y>=this.Height-80
You need to draw the rectangle again to some image for it to display. I created this code for moving and drawing rectangle on pictureBox1, using your already defined circle-rectangle:
Moving the rectangle:
public void MoveRectangle(ref Rectangle rectangle, double angle, double distance)
{
double angleRadians = (Math.PI * (angle) / 180.0);
rectangle.X = (int)((double)rectangle.X - (Math.Cos(angleRadians) * distance));
rectangle.Y = (int)((double)rectangle.Y - (Math.Sin(angleRadians) * distance));
}
Drawing the rectangle and displaying it in the PictureBox:
public void DrawRectangle(Rectangle rectangle)
{
Bitmap bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);
using (Graphics g = Graphics.FromImage(bmp))
{
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.FillEllipse(new SolidBrush(Color.Red), rectangle);
}
pictureBox1.Image = bmp;
}
Demo it with a button click:
private void Button1_Click(object sender, EventArgs e)
{
MoveRectangle(ref circle, 90, 5);
DrawRectangle(circle);
}
Math.Asin(1) * dy is a constant value. Thus, you should use, for example, an instance variable that increments in each Tick of your timer.
...And *dy/dy is irrelevant.
public partial class Form1 : Form
{
Rectangle circle;
double dx = 2;
double dy = 2;
acum=0; //the new variable
...
private void timer_Tick(object sender, EventArgs e)
{
circle.X += (int)dx;
circle.Y += (int)dy;
if (circle.Y>=this.Height-300)
{
dy = -Math.Acos(acum);
acum+=1; //your accumulator
}
this.Refresh();
}
acos and asin are the inverse of sin and cos so the output of those two functions is an angle (usually in radians). This makes the code incorrect.
I strongly suggest that you read up on vector and matrix maths as using Euler angles can get quite messy.
So, you will have a position vector P and a movement vector M and the current position is:
P' = P + M.t
where t is time, P is the original position and P' is the current position.
Then, when you want to change direction, you create a rotation matrix and multiply the movement vector M by this rotation matrix.
The advantage here is that you can step from a 2D system to a 3D system by adding a Z component to your vectors and increasing the size of your matrices.
I have this code:
public class Area
{
Texture2D point;
Rectangle rect;
SpriteBatch _sB;
GameTimer _gt;
int xo, yo, xt, yt;
//List<Card> _cards;
public Area(Texture2D point, SpriteBatch sB)
{
this.point = point;
this._sB = sB;
xt = 660;
yt = 180;
xo = 260;
yo = 90;
}
public void Draw(SpriteBatch spriteBatch)
{
rect = new Rectangle(660, 180, 80, 120);
spriteBatch.Draw(point, rect, Color.White);
_gt = new GameTimer();
_gt.UpdateInterval = TimeSpan.FromSeconds(0.1);
_gt.Draw += OnDraw;
}
private void OnDraw(object sender, GameTimerEventArgs e)
{
this.pass(xo, yo);
if (xo != xt) xo += (xt > xo) ? 10 : -10;
if (yo != yt) yo += (yt > yo) ? 10 : -10;
}
public void pass(int x, int y)
{
rect = new Rectangle(x, y, 80, 120);
_sB.Draw(point, rect, Color.Black);
}
}
So, I can't understand what's wrong. And It's my first project with XNA, and because of it there can be stupid mistake :)
P.S. Sorry. There is a rectangle with coordinates (xt,yt), and I need the animation to move the rectangle to (xo,yo)
P.P.S. I added the full class with corrections, because I don't understand my mistake.
You are drawing the entire animation in one frame.. .you should call Pass with diferent x,y from OnDraw...
EDITED:
1) You don't need the timer, the draw method in game class is by default called 60 frames per second...
2) The Seconds parameter should be calculated as (float) gametime.ElapsedTime.TotalSeconds;
float time;
int xt=660, yt=180;
int xo=260, yo=90;
public void Draw(SpriteBatch spriteBatch, float Seconds)
{
rect = new Rectangle(660, 180, 80, 120);
spriteBatch.Draw(point, rect, Color.White);
this.pass(xo, yo, spriteBatch);
time+= Seconds;
if (time>0.3)
{
if (xo!=xt) xo+= (xt>xo) ? 10 : -10;
if (yo!=yt) yo+= (yt>yo) ? 10 : -10;
time = 0;
}
}
public void pass(int x, int y, spritebatch sb)
{
rect = new Rectangle(x, y, 80, 120);
sb.Draw(point, rect, Color.Red);
}
As you should know this animation will move in a rough mode... if you want to move your sprite smoothly... you can use a Vector2 for your positions and a float for your speed;
Vector2 Origin = new Vector2(260, 90);
Vector2 Target = new Vector2(660, 180);
Vector2 Forward = Vector2.Normalize(Target-Source);
float Speed = 100; // Pixels per second
float Duration = (Target - Origin).Length() / Speed;
float Time = 0;
public void Update(float ElapsedSecondsPerFrame)
{
if (Time<Duration)
{
Time+=Duration;
if (Time > Duration) {
Time = Duration;
Origin = Target;
}
else Origin += Forward * Speed * ElapsedSecondsPerFrame;
}
}
public void Draw()
{
rect = new Rectangle((int) Origin.X, (int) Origin.Y, 80, 120);
sb.Draw(point, rect, Color.Red);
}
If you are willing to use Sprite Vortex to make your animations (a specific version actually) you can use the following class. You have to use Sprite Vortex 1.2.2 because in the newer versions the XML format is changed. Make sure that the XML file you add the property is changed to "Do not compile".
If you need a working example I can send you a very simple one.
p.s. Sprite Vortex should do the same thing you use the other program for, however v 1.2.2 is pretty buggy but not too bad.
the class is here : http://pastebin.com/sNSa7xgQ
Use Sprite Vortex (make sure it's 1.2.2) to select a spritesheet and select the sub images you want to animate. export the XML code.
add the class to your project, it reads the XML and adds automatically creates the frames for the animation for you.