The technique used is based on regions and clipping, i.e. the objects are drawn in a paint event or a forced paintcall from background to foreground, where the smaller objects are painted within their own boundaries. Note that on moving the objects only the area that is affected by the movement needs to be repainted based on the region it occupies on the total canvas. This is highly efficient. There's a bit more to it, but that's the theory.
Controls:
1) Mouse move - topmost sprite is highlighted
2) Left mouse click - bring sprite under cursor to front
3) Left click and drag >5 pixels - drag sprite under cursor
4) Right click - add new random sprite at mouse cursor
You used generic, which is the thing I haven't try. I know how to do BoundingBox test but I haven't known how to solve the alpha issue. In just hours, you can write a working code that solves several advanced problems. I salute you.No, 2005 - is version of QIP (http://forum.qip.ru/). Program was build around 2007, if I remember correctly.
I can start your SmileManager on Linux. :D
Unfortunately nothing more it can do. The add smile feature didn't work and the drag gif file didn't work too. You built it in 2005? Wow, you now must be a very experienced programmer.
Very impressive Mr.Madguy :)Yeah, that's because I can't update just one sprite. When I do it - I also need to update all sprites, visible in this region. But they can't be clipped and therefore their order against sprites, not visible in this region, also breaks. But I actually know, how to solve this problem. If I won't be busy today, I'll improve my program a little bit. May be even implement same algorithm via new control.
I tried doing this myself but could not come up with anything as good as yours, graphics programming is not a strong point of mine but I can look at your code and gain some understanding from it.
There is a slow down with lots of sprites and then moving but you have already explained this. I'm curious as to how it could be optimised, the old Game Maker editor seems to pull it off with hundreds of sprites on screen and moving one causes no slowdown, and that is done using the internal bitmap canvas of a TImage. I wonder how it was able to efficiently do that?
BGRABitmap is a set of units designed to modify and create images with transparency (alpha channel). Direct pixel access allows fast image processing.
Graphics32 is a graphics library for Delphi and Lazarus. Optimized for 32-bit pixel formats, it provides fast operations with pixels and graphic primitives. In most cases Graphics32 considerably outperforms the standard TBitmap/TCanvas methods.
Yes GDI is not great for graphics and game performance, hardware acceleration is the way to go, but this is why I was curious as to how Game Maker could draw and move sprites rather fast on a TImage canvas and of course not disturb the main image, I assume your techninque is not too different to how Game Maker achieved it?Only consoles, like NES, had hardware sprite support. I.e. when you could move sprites completely independently from background. All, you needed to do - to set up some PPU registers. No manual drawing at all. That's why consoles with 8bit 1Mhz processors had better games, than IBM PC. Because CPU driven graphics on 4-plane 1 bit per pixel EGA video adapters - was just complete nightmare.
I think those non-hardware accelerated 2D games use their own graphics engine. Those classic 2D game developers should had developed their own graphics library before OpenGL/DirectX become famous.I guess, this two libraries have software image processing? Mmm. You should understand, that software image processing isn't worth it. In all areas, where software image processing is faster, than GDI - it's better to use graphic libraries, like Direct3D or OpenGL.
Have you consider about BGRABitmap or Graphics32? Here I quote their description:QuoteBGRABitmap is a set of units designed to modify and create images with transparency (alpha channel). Direct pixel access allows fast image processing.QuoteGraphics32 is a graphics library for Delphi and Lazarus. Optimized for 32-bit pixel formats, it provides fast operations with pixels and graphic primitives. In most cases Graphics32 considerably outperforms the standard TBitmap/TCanvas methods.
They both mentioned using fast/direct pixel access.
I wrote simple games for my 8088 machine. Using BIOS graphics functions were too slow. So I wrote my own graphics module using direct mapping to the video buffer. Not very fast (because that cpu is extremely slow) but much better than Turbo Basic default drawing functions and BIOS graphics functions.
That stuff is really cool :)I used cc65 (http://cc65.github.io/cc65/) and runtime libraries from shiru (http://shiru.untergrund.net/articles/programming_nes_games_in_c.htm).
What did you use for writing your code in C? I've always been interested in learning the C family, i tried C# but don't like the .Net stuff and not compiling to native, and C++ is way beyond me.
bit PPUSTATUS
lda cam_position_x
sta PPUSCROLL
lda cam_position_y
sta PPUSCROLL
I.e. you can do it almost for free - no CPU time is spent on it. Yeah, that's, how true hardware acceleration works. 8-)I was wondering how to also delete a sprite under the mouse as I couldn't work it out? %)It's obvious - the same way, you add it. You need to delete sprite from sprite list, update bitmap and make sure you no longer have any references to it (i.e. SpriteUnderCursor and FClickedSprite). Try doing it yourself first.
Also I think I noticed a bug, if lots of sprites are added very close together, when you resize the form the main bitmap changes and then just moving your mouse over the sprites again causes them to change again?Errr. It's due to performance optimization, I've added in last version of my program - I wanted to make resizing faster. That's, what happens, when clipping doesn't work. That's because implicit clipping works for Canvas of control itself, but not for Bitmap. I've fixed it for Windows via adding explicit clipping to Bitmap too. But unfortunately, again, this feature has to be disabled on Linux - all code, related to FUpdateCount, has to be removed. :'( Clipping is needed and as result we have all this problems with it, because I have to use Draw method of Canvas - not CopyRect. Masking doesn't work for CopyRect.
I came up with this for the deletion:
procedure TSpriteControl.DeleteSpriteUnderMouse; var I: Integer; begin if SpriteUnderCursor <> nil then begin for I := Sprites.Count -1 downto 0 do begin if Sprites.Items[I] = SpriteUnderCursor then begin Sprites.Delete(I); Exit; end; end; end; end;
I guess that should be good enough?
EDIT: It seems there is more still to do, as your last comment suggests I need to update the bitmap still as ghost sprites are still left behind...
No. There is simple rule in programming: most obvious algorithm is usually the slowest one. It's just tweaks and optimizations to maximize performance via minimizing amount of redrawing, cuz redrawing - is the most expensive part. And it's iterative process. In my real program even more advanced algorithm is used. As you can see, no sprites are being redrawn at all, if none are updated - we just copy Bitmap to Canvas and that's it. Also clipping allows us to redraw only those sprites, that intersect with current update rect - other sprites stay unaffected. Plus I take old versions of Windows into account. They don't have built-in double-buffering (cuz Canvas has been drawn into temporary texture to be rendered via Direct3D since Vista only) - therefore any direct drawing on Canvas can cause flickering. Plus I try to use minimal amount of memory - just one back buffer. There are more simple, but much slower solutions. Most obvious one - just redraw all sprites every time. Another solution, that is a little bit slower, but should be cross platform - triple-buffering. You should also understand, that if you need to draw background, that is grid of sprites - more simple decomposition algorithms can be used to detect visibility and hit testing.
As for the update I haven't properly tested but it seems ok thanks.
It sounds like the current implementation has limitations as you described with the clipping of the bitmap etc. Anyway you've already done a great amount of work here so you shouldn't push yourself anymore, having said that though if you were to come back when you have some free time and post your crazy idea that would be interesting to see for sure, but again dont feel obliged that you have to I already appreciate your efforts ;)
There is shorter and faster way to do it:
if Assigned(SpriteUnderCursor) then begin Sprites.Remove(SpriteUnderCursor); end;
......
It's not that hard:
1) UpdateSprite(SpriteUnderCursor);
2) if FClickedSprite = SpriteUnderCursor then begin FClickedSprite := nil; FDragging := False; end;
3) SpriteUnderCursor := nil;
Yeah. BIOS functions were slow just because they were altering just one bit in video memory (4-plane 1bpp EGA for example), but had to calculate it's address, bitmask to access it and set up video adapter registers for every single pixel. My library had to overcome this problem too. But still - horizontal moving of sprites and horizontal scrolling were limited to 8-pixel blocks only. It's actually very noticeable in some old DOS games - that horizontal scrolling is quantized.
I.e. what do you have to do to scroll your level on PC? On modern computers you can redraw everything from scratch. Back in that old days CPUs were too weak to do it. You had to shift image via copying it. And even simple memory copying was too slow on my 12Mhz Intel 80286, just because you had to use movsb, that was twice slower, than movsw, just because video adapter latch register was just 8bit wide - it was taking whole CPU time in EGA 640x350 mode (that's why all old games usually used 320x200, besides of having compatibility with VGA 8bpp mode). And what do you have to do on NES? Just 5 instructions:
Umm maybe I did something wrong :-[Try this:
This leaves ghost images behind still sometimes: %)
procedure TSpriteControl.DeleteSpriteUnderMouse; begin if Assigned(SpriteUnderCursor) then begin Sprites.Remove(SpriteUnderCursor); UpdateSprite(SpriteUnderCursor); if FClickedSprite = SpriteUnderCursor then begin FClickedSprite := nil; FDragging := False; end; SpriteUnderCursor := nil; end; end;
Most games still drew sprites at arbitrary coordinates. But it required some additional processing.As I remember, I actually tried all this methods and something didn't work well. That's, what I'm talking about: there was ton of info about adapters themselves, but no working examples of real applications. For example IRQ 2 on retrace would be useful, but it was used as cascade one.
As of scrolling - EGA adapters had special register which controlled starting address of video memory which is visible at screen's top left corner.
Changing that register is a sort of hardware accelerated scrolling. But it scrolled with 8 pixels granularity.
But there was another one register which controlled starting bit or something like this.
Manipulating both of the registers gave pixel-precise scrolling.
I did this - it worked.
Games like Dangerous Dave also used this.
Init;
while True do begin
WaitForFrameStart;
//PPU is now busy - we can do our stuff
ReadJoystics;
DoGameLogic;
//We should prepare this in advance, cuz retrace period is short
FillBuffersWithDataForPPU;
end;
//NMI interrupt - is thing, whole program is built around
procedure OnRetrace;
begin
//PPU is busy during frame rendering
//Short period, when we can send data to it
UpdatePPU;
//Framerate is fixed, so retrace is used for sound timing
//Since PPU is updated, there is no reason for hurrying
//But we shouldn't waste whole processor time
//Some room should be left for game logic
UpdateSound;
end;
Games like Duke Nukem used the "total redraw" method and ran well on my 8MHz PC XTThat's most amazing thing about games from that period of time. Even if you knew asm and low level optimizing, you still couldn't imagine, how such games could be made. But at the same time they existed, so it was possible and you could actually reproduce them from scratch. And this idea was just blowing your mind. Because it was amazing, how it was possible to make such games on machines, that barely could copy data from one place to another within time of one frame.
woah how cool is that 8-) thanks for sharing ;DI don't know. According to Wiki since version 3.0 Game Maker has been using DirectDraw and since version 5.3 it has been using Direct3D. Whether it had been using GDI or software rendering prior to 3.0 - depended on whether it needed advanced rendering features or not. As you can see, GDI can be successfully used for sprite rendering, but at the same time GDI is very limited. Alpha blending isn't supported for example. And via software rendering you can simulate the way, old (even DOS) games worked - i.e. any arbitrary effects. Also hardware capabilities should be taken into account. You should know, that in the past there was some period of time, when video cards were too expensive and not all people had good ones. That's, when software rendering was more widespread. But at some moment load started to shift towards video cards. I.e. hardware acceleration was used in order to free CPU and system RAM for another tasks.
I wouldn't even know where to begin trying to understand it all especially when it comes to working with the bitmap bits and stuff %) %)
EDIT:
So do you think this is more likely how the older Game Makers achieved this especially when the really old versions were before XP days when hardware was really low spec? or would your first examples have been more likely? I guess both seem feasible and no one will ever truly know without completely reverse engineering the logic but trying to get in the mind of another developer and finding out how certain tasks may have been approached and implemented is quite interesting, some of the techniques demonstrated here I would not ever even thought of %)