By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
439,978 Members | 1,362 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 439,978 IT Pros & Developers. It's quick & easy.

Animating one object moving in front of another

P: n/a
I want to animate one object moving in front of another. I cannot re-render
the background as the object moves, as it would be extremely time consuming.

This is what I would like to do. I draw the background to a Graphics object,
copy it, draw the front object on the original, render it to the screen,
then reload the copy (Clone?) of the background for the next loop.

I couldn't find any way of doing this directly, and if there is I would be
very interested.

What I have tried (and have used before) is double buffered graphics. What I
would like to do/have tried is:

BufferedGraphics myBuffer;
BufferedGraphics myotherbuffer;
currentContext = BufferedGraphicsManager.Current;
myBuffer = currentContext.Allocate(pictureBox1.CreateGraphics (),
pictureBox1.DisplayRectangle);
canvas = myBuffer.Graphics;
myotherBuffer=new BufferedGraphics();
myotherBuffer=myBuffer.clone();

**** write to myBuffer and render to screen

myBuffer=myotherbuffer;

and loop.
There are two problems with that. The first is that I don't get a clone
method in my options, and I can't see how to make it happen. The second is
that this must be a problem that occurs all the time, so there should be a
really simple solution.

Can anybody give me the simplest practicable?

Thanks ...
Nov 15 '07 #1
Share this Question
Share on Google+
9 Replies


P: n/a
On 2007-11-15 05:13:58 -0800, "Peter Webb"
<we********@DIESPAMDIEoptusnet.com.ausaid:
I want to animate one object moving in front of another. I cannot
re-render the background as the object moves, as it would be extremely
time consuming.
But you have to, at least somewhere. Windows doesn't natively support sprites.

Disclaimer: I haven't learned anything about WPF yet, and I've heard
that it provides better, more-direct support for animation. You may
want to look into that. It might even include some sort of sprite-like
API.

Disclaimer: also, I'm assuming a normal .NET forms application here.
You may find sprite support in some DirectX API, but a) that would
probably be overkill, and b) it wouldn't be on-topic here so I'm
assuming that's not the way you want to go.
This is what I would like to do. I draw the background to a Graphics
object, copy it, draw the front object on the original, render it to
the screen, then reload the copy (Clone?) of the background for the
next loop.
All very doable. Key things to take into account:

* At some point, you need to draw the background into some
destination and then draw your animation on top of that

* For performance, you'll want to minimize how much drawing you do

To achieve the latter, the best thing is to just do all of this into
the destination form directly. You can set the AllPaintingInWmPaint
style bit to tell .NET that you don't need it to draw the background.
Having set that, then you'll be obligated to draw the entire form
inside the OnPaint() method of your control/form.

Inside the OnPaint() method then, you'll just copy your entire
background image to the control as desired (if your background image
doesn't fill the entire control/form, you'll also have to do a
Graphics.FillRectangle() or similar to clear out the rest of the
background). Then draw the animated object on top of that.

Finally, all of this drawing of background then foreground is going to
cause flicker. So in addition to the above, you'll want to set the
DoubleBuffered property of the control/form to true in the constructor.
This will set things up so that when your own code draws, it's
actually drawing to an off-screen buffer, which will then be copied to
the screen after it's been completely rendered.
I couldn't find any way of doing this directly, and if there is I would
be very interested.

What I have tried (and have used before) is double buffered graphics.
In the context of a form, I have yet to find a situation in which I
actually need the BufferedGraphics class. Setting the DoubleBuffered
property to true has always been sufficient, and then I just write my
code to draw directly to the form.
[...]
There are two problems with that. The first is that I don't get a clone
method in my options, and I can't see how to make it happen. The second
is that this must be a problem that occurs all the time, so there
should be a really simple solution.
All that cloning is just going to slow things down, as is the creation
of a new Graphics instance and allocation of a second BufferedGraphics
instance. Using the technique I describe above, you simply need to
create the source image for the background and foreground once, then
draw them as necessary to the Graphics instance provided in the
OnPaint() method's event arguments.

..NET will manage the remaining buffering, and will presumably do so in
an efficient way.

For best results, make sure that you're only invalidating the areas of
the control/form that have actually changed. :)

Pete

Nov 15 '07 #2

P: n/a
Regarding WPF, it doesn't support sprites, per se, but rather, you can
create animation timelines which will change the properties on ui elements
at timed intervals. WPF contains the entire structure to be rendered in
memory, and handles the invalidation and redrawing of regions whenever
needed.

--
- Nicholas Paldino [.NET/C# MVP]
- mv*@spam.guard.caspershouse.com

"Peter Duniho" <Np*********@NnOwSlPiAnMk.comwrote in message
news:2007111510172143658-NpOeStPeAdM@NnOwSlPiAnMkcom...
On 2007-11-15 05:13:58 -0800, "Peter Webb"
<we********@DIESPAMDIEoptusnet.com.ausaid:
>I want to animate one object moving in front of another. I cannot
re-render the background as the object moves, as it would be extremely
time consuming.

But you have to, at least somewhere. Windows doesn't natively support
sprites.

Disclaimer: I haven't learned anything about WPF yet, and I've heard that
it provides better, more-direct support for animation. You may want to
look into that. It might even include some sort of sprite-like API.

Disclaimer: also, I'm assuming a normal .NET forms application here. You
may find sprite support in some DirectX API, but a) that would probably be
overkill, and b) it wouldn't be on-topic here so I'm assuming that's not
the way you want to go.
>This is what I would like to do. I draw the background to a Graphics
object, copy it, draw the front object on the original, render it to the
screen, then reload the copy (Clone?) of the background for the next
loop.

All very doable. Key things to take into account:

* At some point, you need to draw the background into some destination
and then draw your animation on top of that

* For performance, you'll want to minimize how much drawing you do

To achieve the latter, the best thing is to just do all of this into the
destination form directly. You can set the AllPaintingInWmPaint style bit
to tell .NET that you don't need it to draw the background. Having set
that, then you'll be obligated to draw the entire form inside the
OnPaint() method of your control/form.

Inside the OnPaint() method then, you'll just copy your entire background
image to the control as desired (if your background image doesn't fill the
entire control/form, you'll also have to do a Graphics.FillRectangle() or
similar to clear out the rest of the background). Then draw the animated
object on top of that.

Finally, all of this drawing of background then foreground is going to
cause flicker. So in addition to the above, you'll want to set the
DoubleBuffered property of the control/form to true in the constructor.
This will set things up so that when your own code draws, it's actually
drawing to an off-screen buffer, which will then be copied to the screen
after it's been completely rendered.
>I couldn't find any way of doing this directly, and if there is I would
be very interested.

What I have tried (and have used before) is double buffered graphics.

In the context of a form, I have yet to find a situation in which I
actually need the BufferedGraphics class. Setting the DoubleBuffered
property to true has always been sufficient, and then I just write my code
to draw directly to the form.
>[...]
There are two problems with that. The first is that I don't get a clone
method in my options, and I can't see how to make it happen. The second
is that this must be a problem that occurs all the time, so there should
be a really simple solution.

All that cloning is just going to slow things down, as is the creation of
a new Graphics instance and allocation of a second BufferedGraphics
instance. Using the technique I describe above, you simply need to create
the source image for the background and foreground once, then draw them as
necessary to the Graphics instance provided in the OnPaint() method's
event arguments.

.NET will manage the remaining buffering, and will presumably do so in an
efficient way.

For best results, make sure that you're only invalidating the areas of the
control/form that have actually changed. :)

Pete

Nov 15 '07 #3

P: n/a
I knew this would happen. I have simplified my actual problem to explain it
more easily, and lost a key constraint.

Here is what I would really like to do.

I am writing an educational orbit simulator. Basically there are several
bodies in mutual orbits, and I want the display to show the tracks (orbits)
the centre of the bodies follow as they move around. These tracks are
dynamically generated as the circles move. The problem is that the circles
representing the bodies obscure the tracks they are creating, and as soon as
I draw the circles for the objects I wipe out the tracks underneath.

Lets imagine I have a background that shows the orbit tracks so far. In my
animation loop, I want to:

1. Calculate the new positions for the circles, which will be typically
moved a pixel or so between frames.

2. Add the few pixels to the background which shows the movement of the
centre of the circles since the last frame.

3. Save this.

4. Draw the circles on top of the tracks and pause

5. Reload the background image which shows the background tracks but not the
circles.

6. loop

In other words, I want to update my background (the tracks so far) and write
it to a temporary graphics object, save a copy of this object, draw my
circles on top, display it, and then reload the background without the
circles for the next loop.

It all seems pretty simple, if only I can take a copy of the whole
background (or at the parts obscured by the circles), update it with the
extra little bit of tracks, draw the background tracks, then draw the
circles in their new positions on top.

I know how to use double buffered graphics to eliminate flicker by drawing
the background, drawing the foreground, then rendering to the screen. This
all works for one circle moving in front of another; I draw new circles on
top of the old circles of the same size but in the background colour (which
deletes the circles from the display), then draw the new circles in the
correct position from back to front. Its all pretty well optimised, and
only forces redraws if the circles actually move and only redraws the screen
parts that have actually changed. However this unfortunately blanks out the
background (track information) as well.

As I said, this would seem to be a reasonably common problem, and it seems
that I should be able to have two Graphics buffers - one for the background
alone which I update with the orbit tracks, and a copy of this that I write
the new position of the circles onto for the purpose of display.

So the solution you have identified doesn't seem to work, as the background
is not a constant image - it needs a few pixels added each time to show the
update tracks. You don't actually see this new track background until the
circle moves and stops obscuring it.

Hence my desire to clone the current version of the background image (the
tracks) - it changes slightly on each frame, and it would be completely
impractical to attempt to regenerate all past orbital track information on
every frame.

Any ideas?

(And thanks for your help so far)

PS I have never even heard of WPF, but what I want to do sounds so simple
and common that I think there must be a very simple answer - just being able
to copy a Graphics buffer should solve my problem

Nov 16 '07 #4

P: n/a
On 2007-11-15 16:40:16 -0800, "Peter Webb"
<we********@DIESPAMDIEoptusnet.com.ausaid:
[...]
So the solution you have identified doesn't seem to work, as the
background is not a constant image - it needs a few pixels added each
time to show the update tracks. You don't actually see this new track
background until the circle moves and stops obscuring it.
Given your description, I personally would not bother with maintaining
a copy of the background at all. Instead, I would just keep a list of
the points defining the orbital tracks, and redraw the tracks each time
for an update. I know it sounds like a lot of drawing effort, but in
reality the time taken to redraw the tracks from scratch will be
inconsequential.

That said, nothing about my proposal precludes what you're trying to
do. Assuming you've got a background image cached somewhere, there's
nothing to stop you from updating it as necessary if and when the
background is supposed to change as well. There's no point in that
process in which you'd need to clone the existing background though.
[...]
PS I have never even heard of WPF, but what I want to do sounds so
simple and common that I think there must be a very simple answer -
just being able to copy a Graphics buffer should solve my problem
Copying the background buffer is overkill. Also, IMHO you should be
more precise about your terminology. There's not really anything
called a "Graphics buffer" in .NET. There's a Graphics object, which
is a sort of portal into a buffer (like a Bitmap, Metafile, or even
BufferedGraphics), and there's a BufferedGraphics (which you obviously
know about). It's not really clear when you write "Graphics buffer"
which you're talking about, if either.

Pete

Nov 16 '07 #5

P: n/a

"Peter Duniho" <Np*********@NnOwSlPiAnMk.comwrote in message
news:2007111518505016807-NpOeStPeAdM@NnOwSlPiAnMkcom...
On 2007-11-15 16:40:16 -0800, "Peter Webb"
<we********@DIESPAMDIEoptusnet.com.ausaid:
>[...]
So the solution you have identified doesn't seem to work, as the
background is not a constant image - it needs a few pixels added each
time to show the update tracks. You don't actually see this new track
background until the circle moves and stops obscuring it.

Given your description, I personally would not bother with maintaining a
copy of the background at all. Instead, I would just keep a list of the
points defining the orbital tracks, and redraw the tracks each time for an
update. I know it sounds like a lot of drawing effort, but in reality the
time taken to redraw the tracks from scratch will be inconsequential.
Not in this case.

We might have 4 or 5 bodies (circles) moving, and on each frame they may
move only one or two pixels. After the animation has been running for a
minute or so, each track may comprise many thousands of very short lines
each of one or a couple of pixels length, each independently drawn. I want
the tracks to look nice, so I will be using anti-alias and similar. This
means between each screen refresh, I will potentially have to draw thousands
of very short line segments. I can optimise this by seeing if the segment
has been revealed by a moving circle, but this test will probably take as
long as just re-drawing it. The animation will get slower and slower as
there are more lines to draw between each frame.

That said, nothing about my proposal precludes what you're trying to do.
Assuming you've got a background image cached somewhere, there's nothing
to stop you from updating it as necessary if and when the background is
supposed to change as well. There's no point in that process in which
you'd need to clone the existing background though.
I can't just cache a background copy, because I want to draw to this using
the Graphics methods. There does not seem a direct facility to draw an
object but not display it, unless I use double buffered graphics.
>[...]
PS I have never even heard of WPF, but what I want to do sounds so simple
and common that I think there must be a very simple answer - just being
able to copy a Graphics buffer should solve my problem

Copying the background buffer is overkill. Also, IMHO you should be more
precise about your terminology. There's not really anything called a
"Graphics buffer" in .NET. There's a Graphics object, which is a sort of
portal into a buffer (like a Bitmap, Metafile, or even BufferedGraphics),
and there's a BufferedGraphics (which you obviously know about). It's not
really clear when you write "Graphics buffer" which you're talking about,
if either.

That's because I don't know how to do what I want, so I can't use proper
terminology.

The code snippet I showed before illustrates one possible approach:

BufferedGraphics myBuffer;
BufferedGraphics myotherbuffer;
currentContext = BufferedGraphicsManager.Current;
myBuffer = currentContext.Allocate(pictureBox1.CreateGraphics (),
pictureBox1.DisplayRectangle);
canvas = myBuffer.Graphics;

loop:
{

*** write the new track points to myBuffer

myotherBuffer=new BufferedGraphics();
myotherBuffer=myBuffer.clone(); // save a version of the background with
new tracks in myotherBuffer

**** write the circles to myBuffer and render to screen

myBuffer=myotherbuffer; // reloads the copy of the background without the
circles

}

This would work fine, and just involves copying a bitmap to a new location.

Unfortunately, I can't clone myBuffer, and if I just go:
myotherbuffer = mybuffer;
I am updating the object ref, not copying the object.

There must be a simple way to do this. All I am really trying to do is
generate two drawing layers - one for the foreground, one for the
background. You see this is every drawing package I have ever used, and its
a very common requirement. It would seem strange if there wasn't some way of
emulating two layers in some manner - even if it is not through the
buffering "trick" that I have described (and which unfortunately doesn't
work).
>
Pete

Nov 16 '07 #6

P: n/a
Thanks for your multiple suggestions. I will try them out.

One thing I am curious about. You do say:

"Well, it's fairly easy to make a copy of a Bitmap or BufferedGraphics
instance, but you don't need to."

If I just wanted to copy my BufferedGraphics instance - which is where I
started - how would I do it? This still seems a very simple way of doing it.
I write my updated track information to mytrackbuffer, copy this to
mytemptrackbuffer, draw my foreground, then copy mytemptrackbuffer back to
mytrackbuffer. This reduces the processing to a simple clone of a
BufferedGraphics instance - copying a bitmap - which presumably the system
can do way faster through a .clone() type command than I can do
programmatically.

In the mean time, I will have a play with Bitmaps. I will also investigate
OnPaint; my code doesn't currently use that technique.

And thanks again.

Peter Webb
Nov 16 '07 #7

P: n/a
On 2007-11-16 01:08:38 -0800, "Peter Webb"
<we********@DIESPAMDIEoptusnet.com.ausaid:
Thanks for your multiple suggestions. I will try them out.

One thing I am curious about. You do say:

"Well, it's fairly easy to make a copy of a Bitmap or BufferedGraphics
instance, but you don't need to."

If I just wanted to copy my BufferedGraphics instance - which is where
I started - how would I do it?
It's the same as drawing into the buffer in the first place, except
that the source image is the other buffer (whether BufferedGraphics or
Bitmap or whatever). Actually, it's even easier for the Bitmap class,
but even if it wasn't, it wouldn't be hard.

Still, making a copy isn't going to be helpful in this situation. All
it will do is slow things down. It's not even simpler; if you make a
copy, you are still doing all of the same work I suggest, except you're
throwing in an extra blt for no good reason.
This still seems a very simple way of doing it. I write my updated
track information to mytrackbuffer, copy this to mytemptrackbuffer,
draw my foreground, then copy mytemptrackbuffer back to mytrackbuffer.
This reduces the processing to a simple clone of a BufferedGraphics
instance - copying a bitmap - which presumably the system can do way
faster through a .clone() type command than I can do programmatically.
I agree the system can probably copy the buffer faster, which is why
you should leave the double-buffering up to the OS. That's what you're
doing with the copy, and it's inefficient. There's no point to it.
Leave "mytemptrackbuffer" out of the above process, and it will still
work except you don't have the extra copy from your code.
In the mean time, I will have a play with Bitmaps. I will also
investigate OnPaint; my code doesn't currently use that technique.
Well, your code should at a minimum be using the Paint event. If not,
you have problems other than just the question of getting the output
you desire.

If your code is using the Paint event, there's very little difference
between that and override the OnPaint() method. Everything I wrote
still applies, and you don't need to switch from the Paint event to
overriding OnPaint() to take advantage of the technique.

Pete

Nov 16 '07 #8

P: n/a
Thankyou for your information and suggestions.

Having now tried at least part of them, I have a technique which works, and
I now understand a little more of what is going on.

I didn't realise that you could set up a a Bitmap as a Graphics instance. I
write my background track information to the Bitmap, render the Bitmap, then
draw my circles on top. Seems to work great. This reduces the additional
processing to a single blt, which is great (and as you indicated would
happen). So my problem appears solved.

Also, during testing, I noticed an interesting thing about the Bitmap class.
The default constructor appears to set the transparency of each pixel to
100%. This means that if I draw something to a Bitmap and then render the
bitmap, only the objects that I have drawn to the bitmap are overlayed (ie
there is no no change to the background). This is actually perverse
operation in my case, because my Bitmap is the background, and I will need
to suppress this through a clearscreen(backgroundcolour) call to initialise
the bitmap when it is created (trivial, of course). It does raise some
interesting possibilities for the future, because the use of transparency
almost directly allows sprites to be created. My code isn't written that
way, but its great to know.

I don't use OnPaint or any Paint commands. I implement my Graphics instances
directly through CreateGraphics commands. This is the first technique I got
to work, and so naturally it will be the technique I will always use unless
it stops working. Unless you can identify a reason to change, I won't.

This is my first serious foray into programming in 30 years. I have been at
this now for about a month, and the first 2 weeks was spent with almost no
idea as to what a "class" actually is. This OO stuff was completely new to
me. When I started, everything was difficult, because I was always "fighting
against" how C# wanted to work. Now I seldom have this problem.

If not for this newsgroup, I would probably still be doing "Hello Worlds". I
am very grateful indeed to you and the other people that have helped - this
newsgroup is one of the best reasons to use C#. Your time and insights have
been appreciated very much; thankyou.

Peter Webb
Nov 17 '07 #9

P: n/a
On 2007-11-16 17:50:57 -0800, "Peter Webb"
<we********@DIESPAMDIEoptusnet.com.ausaid:
[...]
Also, during testing, I noticed an interesting thing about the Bitmap
class. The default constructor appears to set the transparency of each
pixel to 100%. This means that if I draw something to a Bitmap and then
render the bitmap, only the objects that I have drawn to the bitmap are
overlayed (ie there is no no change to the background). This is
actually perverse operation in my case, because my Bitmap is the
background, and I will need to suppress this through a
clearscreen(backgroundcolour) call to initialise the bitmap when it is
created (trivial, of course).
You may prefer then to provide an explicit pixel format when creating
the bitmap, and make sure it's a 24-bit-per-pixel bitmap instead of a
format that supports alpha (such as 32-bpp, which is probably what
you're getting now).

Of course, that's only really helpful if you want your background to be
black. But at least you get that default black background instead of
see-through. :)
It does raise some interesting possibilities for the future, because
the use of transparency almost directly allows sprites to be created.
My code isn't written that way, but its great to know.
Yes, GDI supports alpha (transparency) reasonably well. Unfortunately,
the Windows control classes don't; they always paint a background, so
even if you draw a transparent bitmap into the control, you can't see
through it to other controls underneath.

So, transparency works great _within_ a specific control, but don't
expect to easily be able to layer overlapping controls in a form and
have that work okay, or even to have transparent controls on top of a
form with a custom-drawn background.
I don't use OnPaint or any Paint commands. I implement my Graphics
instances directly through CreateGraphics commands. This is the first
technique I got to work, and so naturally it will be the technique I
will always use unless it stops working. Unless you can identify a
reason to change, I won't.
You need to change.

First, based on the code you posted, I think there's a good chance
you're not disposing the creating Graphics instance properly. You need
to learn to do that anyway (it applies to the Graphics you get from the
Bitmap as well, and possibly other objects you create...basically, if
the object implements IDisposable, you need to dispose it), but you can
avoid at least that part of the problem just by moving to a more
appropriate rendering method.

As for a more specific reason to change, it's basically a matter of
sticking with the approved paradigm in Windows. You don't draw
whenever you feel like it. You draw when Windows tells you to. Doing
it any other way will result in a variety of bugs where you either have
trouble figuring out how to draw exactly what you need to at the
appropriate time, or you just don't do it at all.

A common way to expose such bugs is to drag windows on the screen
around, either moving other windows over your own, or moving yours
around so that it becomes partially obscured (such as off the screen).

Even when you have to draw because you know you've changed the data to
be displayed, you don't do that directly. You invalidate the area on
your window (form) that has changed (sometimes this is the entire
window, but if you know it's less than that, you get much better
performance only invalidating the area that changed). At an
appropriate time, you'll get a window message (event) telling your code
to draw, which is handled either in the OnPaint() method, or in a Paint
event handler.

You know how you write "I was always 'fighting against' how C# wanted
to work"? This is an example of that. Right now, you are fighting
against how Windows wants to work, and it's going to cause problems.
If not now, then eventually, and likely sooner rather than later.
This is my first serious foray into programming in 30 years. I have
been at this now for about a month, and the first 2 weeks was spent
with almost no idea as to what a "class" actually is. This OO stuff was
completely new to me. When I started, everything was difficult, because
I was always "fighting against" how C# wanted to work. Now I seldom
have this problem.
For what it's worth, a suggestion: when you receive some advice in this
newsgroup, if you are thinking to yourself "no, that won't work for
me", your first thought after that should be "hmm...maybe it will, and
I'm just misunderstanding".

We can work through the misunderstanding, but it goes a lot faster if
you don't start out with the assumption that the person trying to help
you doesn't actually have the answer. They won't always, granted, but
they often will and telling them that what they are trying to offer
isn't going to help just makes it take longer to get to the useful
point.

This is especially true given your lack of experience. Based on your
explanation, I'd say you have a _long_ way to go before your intuition
about whether someone's advice is actually applicable or not can be
trusted. You might as well give a person the benefit of the doubt
before telling them what they're offering won't work.
If not for this newsgroup, I would probably still be doing "Hello
Worlds". I am very grateful indeed to you and the other people that
have helped - this newsgroup is one of the best reasons to use C#. Your
time and insights have been appreciated very much; thankyou.
You're welcome. :)

Pete

Nov 17 '07 #10

This discussion thread is closed

Replies have been disabled for this discussion.