I'm trying to build a simple RPG using the C# Windows Forms (as I had recently stumbled upon a fun tutorial demonstrating this ability. I have two items:
A 'Character' object that has been placed IN game:
In-game character--GUI
I also have a code-generated Draw Object, a tree--built in-game:
public void MainFormPaint(object sender, PaintEventArgs e)
{
//Drawing a tree, to create transparency
Image Tree_2 = Image.FromFile("[Directory to PNG].png");
Tree_2.Tag = "Tree";
e.Graphics.DrawImage(Tree_2,50,50,200,200);
}
...which generates this:
Code-generated Tree Object
Seeing as I cannot detect the object by some means similar to:
Character.Bounds.Intersectswith([insert_my_picture].Bounds);
this leaves me kind of baffled, and I'm not sure what to do. I want to detect this collision, so that I can stop movement. However, I'm not sure how to check for an 'empty spot' next to me or any object for that matter that's code-generated. It's important to note that this image is code-generated to maintain the graphic's transparency (as apparently there are problems placing objects in the form and maintaining transparency with overlaying objects).
Thank you for your help!
As the graphic was drawn at runtime, I was needing to generate a Rectangle:
Rectangle firstTree = new Rectangle();
...in the public variables area, and then created it on paint event.
public void MainFormPaint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(Tree_Obj,50,50,200,200);
firstTree.X = 50;
firstTree.Y = 50;
firstTree.Width = 200;
firstTree.Height=200;
}
The full, encapsulating bounds were only for trial. Problem solved!
Related
I am adding two different images on win2d canvas in different points and different size and run application display two images perfect points set to display. Then how I am select image and move on canvas.
Win2D is an immediate mode graphics library (from Wikipedia)
Immediate mode rendering is a style for application programming interfaces of graphics libraries, in which client calls directly cause rendering of graphics objects to the display. It does not preclude the use of double-buffering. In contrast to retained mode, lists of objects to be rendered are not saved by the API library. Instead, the application must re-issue all drawing commands required to describe the entire scene each time a new frame is required, regardless of actual changes. This method provides the maximum amount of control and flexibility to the application program.
So it's up to you to keep the reference of any object you want to modify, because once it's drawn it is lost.
So define your CanvasBitmap as a global resource or create some type of ResourceLocator. Then create a your own class that stores x,y,width,height kinda like a custom object;
public class GenericItem
{
public CanvasBitmap b;
public int x;
public int y;
public int w;
public int h;
}
Modified Example from Win2D:
CanvasBitmap cat, mouse;
GenericItem gi_cat;
load your bitmaps in:
async Task CreateResourcesAsync(CanvasControl sender)
{
cat = await CanvasBitmap.LoadAsync(sender, "ShawnsCat.jpg");
mouse = await CanvasBitmap.LoadAsync(sender, "Mouse.png");
// create your GenericItem here
gi_cat = new GenericItem();
// fill in your x,y,width,height,bitmap
}
now draw
void myWidget_Draw(CanvasControl sender, CanvasDrawEventArgs args)
{
args.DrawingSession.DrawImage(gi_cat.b, gi_cat.x, gi_cat.y);
}
now you can modify gi_cat.x gi_cat.y and whatever property you added.
gi_cat.x = 500;
gi_cat.y = 250;
and you can cause a redraw calling the Invalidate Method on the canvas control.
name_of_your_canvas.Invalidate();
which will cause the canvas control to redraw with the new position.
Basically you have to handle everything yourself. If you looking for a DOM like approach then just use the regular Canvas control available in XAML.
I have a pretty in depth Win2D Walkthrough here :
Win2D Getting Started: Windows Universal Application
In my setup i have 5 pictureboxes which I am trying to layer.
My code for this is:
pbCoin1.Parent = pbMap;
pbCoin1.BackColor = Color.Transparent;
pbCoin2.Parent = pbMap;
pbCoin2.BackColor = Color.Transparent;
pbCoin3.Parent = pbMap;
pbCoin3.BackColor = Color.Transparent;
pbFlashlight.Parent = pbMap;
pbFlashlight.BackColor = Color.Transparent;`
All 5 pictureboxes contain images. The method I am using works fine, but the problem is that the PbCoin 1,2,3 are glitching trough my pbFLashlight(see image).
Can someone provide a solution such that the coins are only visible when the transparent part of the black layer is over it?
Don't use multiple picture boxes. Use a single PictureBox to represent the game area and handle the .Paint event. Do all of your drawing using GDI calls on the Graphics reference that .Paint sends you in e. You will want to call PictureBox.Invalidate at the end of the Paint event to cause it to queue up the next frame
For better performance, create a new Bitmap instance that you keep a reference to for the life of the game. Clear and do all your drawing to that bitmap every frame, then draw that bitmap to the PictureBox in .Paint.
For even better performance than that, don't use WinForms.
The basic pattern should look like this:
private void Canvas_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(...);
Canvas.Invalidate();
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
How can a drawing canvas be realised in managed C++ (C++/CLI). I want to be able to "draw" on a PictureBox object by leftclicking the mouse and simulatneous moving the mouse. The PictureBox is cleared with white color and i want to draw with Black pixels.
First of all, consider this an exception. While it's perfectly fine to ask for help with assignments and homework related stuff here (just make sure to mention it), you should really try to show some code or work you've done already. Don't just go like "I need some code, thanks." because that's not how this site is meant to work.
Please don't just copy and paste this code. Understand it first, then use it or apply what you've learned to your own code.
Back to the actual problem: There are multiple ways to approach this, but the basic concept is always the same (even if you try to create some vector drawing program). The following lines ommit classes and namespaces for readability. If you keep the standard using directives, this shouldn't be an issue for you (most stull will be in System.Drawing). Note that I'll implement everything directly into the form. You could as well create a custom user control for this (which might be the better/cleaner approach).
First you'll need some control to actually display your drawing. Using the standard control PictureBox is perfectly fine for this.
Next you'll need some image to actually draw to. Add a private Bitmap member to your user form.
In this example we'll use the mouse cursor to draw a line. A line is always drawn between the previous position of the cursor and the current position. Due to this we'll have to store the previous position in a Point.
Given the previous two points, you'll need the following two members somewhere in your form (or user control):
private Bitmap bitmap;
private Point oldPosition;
In your form's Load event you'll have to create the Bitmap object. I'm also using Graphics to clear the Bitmap to white and then display it using the PictureBox:
private void Form1_Load(object sender, EventArgs e)
{
bitmap = new Bitmap(pictureBox1.Width, pictureBox1.Height);
using (Graphics g = Graphics.FromImage(bitmap))
g.Clear(Color.White);
pictureBox1.Image = bitmap;
}
Next up, we'll have to reset the previous cursor position, whenever the user clicks somewhere in the PictureBox. For this I add a very simple MouseDown event to it:
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
oldPosition = e.Location;
}
Last but not least, the actual drawing happens in an MouseMove event. For this to work properly, you'll have to do one check and three working steps:
Determine whether the user actually wants to draw (is the left mouse button pressed?).
Draw the line into the bitmap.
Update the previous cursor position to the new position.
Display the results.
The code for this could look like this:
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
using (Graphics g = Graphics.FromImage(bitmap))
g.DrawLine(Pens.Black, oldPosition, e.Location);
oldPosition = e.Location;
pictureBox1.Image = bitmap;
}
}
If everything works as expected, you should be able to draw freehandedly on your PictureBox:
How do I draw a line and assign events to it? I would like to draw a custom shape, but it should act like a normal Control, and have properties and events. E.g.:
button1_Click(object sender, EventArgs e)
{
DrawLine(width, height, location, location, color, panelToDrawShapeOn, nameThisShape);
}
nameThisShape_Click(object sender, EventArgs e)
{
MessageBox.Show("Click event raised.");
}
private void DrawLine(int width, int height, int location, int location, Color color, Panel panel, string controlName)
{
// Code to draw shape and setup events for it.
}
To confirm, I do know how to draw shapes using GDI+ but the problem is, they are static, and I can't "interact" with them, and no amount of searching has led me to the right place to find out how to interact with the shapes that I draw.
You're not going to be able to treat custom-drawn shapes as you would controls. As you've found, GDI+ is an immediate mode graphics system (as opposed a retained mode system). This means that if you want a persistent scene graph full of shapes to be rendered, you need to create and manage that yourself. Then, you would hook the events of interest on the control that's the drawing target and handle them by doing hit tests on your list of renderable objects (e.g., to find what shape, if any, the mouse is over).
Writing that code can be a lot of work, but you can find libraries to help you. For instance, in one of my work projects, we used a computational geometry library called JTS for the geometry representation and hit test code. If you want to avoid third-party libraries, you may get part of the way there with the Region class, which will at least give you hit tests.
I need to allow to the user to draw lines over an bitmap. Lines should be drawn interactively, I mean something performed using typical code giving to the user a visual feedback about what is drawn:
private void MainPictureBox_MouseDown( object sender, MouseEventArgs e)
{
DrawingInProgress = true ;
Origin = new Point (e.X, e.Y);
}
private void MainPictureBox_MouseUp(object sender, MouseEventArgs e)
{
DrawingInProgress = false ;
}
private void MainPictureBox_MouseMove(object sender, MouseEventArgs e)
{
if (!DrawingInProgress) return ;
End = new Point (e.X, e.Y);
using( Pen OverlayPen = new Pen( Color .Red, 1.0f))
using (Graphics g = MainPictureBox.CreateGraphics())
{
g.DrawLine(OverlayPen, Origin, End);
}
}
Of course I keep track of the points using List.Add within MainPictureBox_MouseUp in order to draw lines in the Paint event (code not shown for the sake of simplicity)
Without the background image things could be done nicely simply overwriting the previous line with the background color, something like:
g.DrawLine(BackgroundColorPen, Origin, PreviousEnd);
g.DrawLine(OverlayPen, Origin, End);
but this is not possible with a not uniform background.
Invalidating the rectangle defined by the points: Origin, PreviousEnd then using Update() makes the rendering quite messy. I am wondering how to perform this task and those are possible ways to do so i am considering:
Draw the lines over a transparent bitmap then draw the bitmap over the Picturebox. I guess that with big images this is simply unfeasible for performances reason.
Using the Picture.BackgroundImage for the bitmap then drawing on the Picture.Image but I unable to figure out how this could really saave the day
Using double buffering? How?
Stacking a different control (a panel?) over the pictureBox, making it transparent (is it possible?) then drawing over it.
Could someone give a hint in the best direction? I am really getting lost.
The solutions working for me has been the following:
Create a transparent panel;
Put it over the bitmap having them overlap completely;
Draw on the panel using proper mouse events;
There is no need to cancel the previous shape, of course: it was a misleading question. It is sufficient to distinguish permanent shapes recorded in proper lists fed to the Paint event from the transient shape previously drawn that will be not drawn again in the next Paint event;
Make absolutely sure that all drawings are performed in the Paint event using the Graphics provided by the PaintEventArgs. Thanks to #HansPassant to have stressed this in a different post.