Canvas.paint has an auto-refresh built in?

Hi there!

As you know, I’m doing some improvements on my game.

instead of paint “all” in only one layer, i’m painting the background, the maze, the title on a myCanvas_layer0 and the snacks, cat, mouse on myCanvas (this is for now, for tests…)

there are N-O-T-H-I-N-G calling a refresh on myCanvas_layer0… so why paint event is called all the time?

canvas.paint has an auto-refresh built in?

this next two images, are without and with a layer, showing the number of calls to draw the maze walls. i put a quit just after the last canvas has paint.


as expected, both testes has the same number of calls… 53, because there are 53 lines to draw.

then, i created a 5s timer, to quit the game…

again, without and with a layer:


why the c_wall.paintWalls on myCanvas_layer0 was called 134 instead of only 1?

WHY?

Alex

I think you are doing it wrong with ‘layers’. Take a look at the little Asteroids game I made.
Use a timer set to 13 (60fps) and draw the objects.
No need to refresh the canvas the way you are doing. That’s just overloading and way too slow.

myCanvas_layer0.paint()

g.DrawingColor = gameBackgroudColor
g.FillRectangle(0, 0, g.Width, g.Height)

c_game.paintWalls(g)
c_game.paintScoreText(g) // "SCORE:"
c_game.paintMiceText(g) // "MICE:"

why it’s called all the time, if there are no call to myCanvas_layer0.refresh?

when canvas.refresh is called, it forces all canvas to refresh? or the window to refresh?

Paint is triggered by the refresh, it does not cause a refresh. Your canvas can be refreshed at other times too, such as when switching focus or when another window passes over it. Use paint as “here’s what should be on the canvas at this point in time” and the refresh won’t matter.

Usually I recommend not ever writing to any properties inside a paint event. It’s a view-only event. There have been times when I’ve broken that rule, but it’s always made my life more difficult.

3 Likes

it should be something like this to paint in to a picture, its not a event.
CreateStaticLevelBackground(BackgroundPicture.Graphics)

search your project myCanvas_layer0 and see why it is called so often.

1 Like

i’m trying to find what is calling…

i’m not crazy!

refresh bug.xojo_binary_project.zip (4.9 KB)

omg…

when one canvas “touches” or are over the other, any “.refresh” refreshes all canvas that are in contact with the canvas was refreshed!

is this a bug? for me, yes. it kills my idea of layers

https://tracker.xojo.com/xojoinc/xojo/-/issues/79735

Take a look there, may be a must read for you (there are other books).

How I wish… Canvas

It is not a bug.

You should perform all of your drawing in a single canvas instead of trying to overlap controls. There was an example project that demonstrated this. Not sure if it is still included.

3 Likes

Perhaps you misunderstood the advice you;ve been given. Instead of using multiple Canvas controls to stack your drawing, use multiple Picture objects. That reduces your Paint event to a couple of g.DrawPicture calls. As well as drawing the parts that change all the time, like the mouse and cat.

3 Likes

For example, in Paint you have c_game.paintWalls(g). You want to replace that call with a pre-rendered picture. Fortunately, you’re passing g as graphics to the method, which means the method doesn’t care where g came from. So add a property wallPicture as Picture to the window and in the window Opening event

wallPicture = new Picture(width, height)
c_game.paintWalls(wallPicture.Graphics)

You only call paintWalls once to render the walls and then use the picture in the canvas paint event instead of paintWalls.

3 Likes

do not overlap controls, use one single canvas as mentioned.
a canvas has no buffer, if it get invalid because a window or something overlap and move
it start the paint event for a refresh/redraw.

should be part of the game class and create at game start / level change via method

c_game.wallPicture = new Picture(width, height)
c_game.paintWalls(wallPicture.Graphics)
2 Likes

You should only use one canvas. Not canvas layers.
I said it before and say it again - you are doing it wrong.
And again .. check the Asteroid game I provided. It should give you a lot of pointers how to create simple games running smoothly (60fps)

2 Likes

we should ask xojo to make next month a retro game contest :slight_smile:
at Year of Code 2025

The way you think layers should be “offscreen” using your own sets of objects and painting just one large composed view canvas at the end.

2 Likes

i think it could be one layer labyrinth walls, score text, lives, level number, the collect pieces via one method which return a picture. (with your sub methods for drawing the parts)
the moving mouse and cat on top in every frame.
and if the mouse collect one item redraw into a static picture again,
if she not collect draw the same picture again because nothing change.

I looked at the list of books, and I found this one: Threads and timers.

Here’s the web page intro text:
This book “I Wish I Knew How to … Program Threads and Timers with Xojo Desktop” shows you how to use timers to refresh the GUI and threads to perform background tasks with your computer to increase the program execution speed.

ok…

g.DrawingColor = gameBackgroudColor
g.FillRectangle(0, 0, g.Width, g.Height)

// c_game.paintWalls(g)
// c_game.paintScoreText(g)
// c_game.paintMiceText(g)

wallPicture = New Picture(g.Width, g.Height)
c_game.paintAll(wallPicture.Graphics)
g.DrawPicture(wallPicture, 0, 0, g.Width, g.Height, 0, 0, wallPicture.Width, wallPicture.Height)

this way? inside the canvas.paint() event, right? because i need to drawPicture on “g”, right?

but if i do this, it’ll be called all the time, not once…

what i’m missing?

Because if i do this way, the problem is the same. every time the refresh is called (on other canvas), it’ll refresh this one!

Yes, that happens.
Avoid overlapping controls at all cost.

Render all the painting to an in-memory picture, and paint that picture to the canvas in the paint event.
Invalidate the canvas instead of refreshing, and it will only redraw when it needs to.