English Amiga Board


Go Back   English Amiga Board > Coders > Coders. Language > Coders. Blitz Basic

 
 
Thread Tools
Old 27 December 2018, 12:48   #1
earok
Registered User
 
Join Date: Dec 2013
Location: Auckland
Posts: 3,539
Kiwi's Tale dev notes

Just some rough notes about how I made the underlying engine. Happy to answer any questions.

Scrolling is 8-way using a hybrid splitscreen (vertical) and corkscrew (horizontal) setup - so each tile is blitted once during a scroll, there's no "blitting at both sides of the screen" techniques used. Corkscrew has been covered elsewhere so I'll just briefly touch on it here - the core idea is that your bitmap is treated as one long bitmap, wrapping around to the next line on the edges (when the display goes off the right end of the bitmap, it wraps around to the left but on the next line down).

Something I found useful to do was to alter the _xclip and _yclip values of the bitmap so that I could blit with the debugger attached and not have Blitz complain that I was blitting over the x-edge.

Code:
;Fake increase the X clip size
*b.bitmap = Addr Bitmap(2+i)
*b\_xclip = $7FFF
Splitscreen scrolling works on a similar concept but it's a fair bit more complicated, since you can't just blit or show the display over the bottom edge and expect it to wrap around to the top. Here, copper tricks are needed. I'm using the RICOPPERFX library to help.

Code:
wrappedCameraY = CameraY mod #BufferHeight
DisplayBitMap #CopList_Main,FrontBuffer,CameraX,wrappedCameraY
splitline = #BufferHeight - wrappedCameraY
CopperReset #CopList_Main, 0

;if we're displaying over the bottom edge
if splitline < 208

	;scroll to the right position
	splitoffset = CameraX / 8

	;wait until the bottom edge       
	CopperWait 0, splitline + 44         

	;reset the display to the top of the bitmap
	bp.l = *b\_data[0] + splitoffset
	CopperMove $0e0, bp lsr 16
	CopperMove $0e2, bp & $ffff
	bp.l = *b\_data[1] + splitoffset
	CopperMove $0e4, bp lsr 16
	CopperMove $0e6, bp & $ffff
	bp.l = *b\_data[2] + splitoffset
	CopperMove $0e8, bp lsr 16
	CopperMove $0ea, bp & $ffff
	bp.l = *b\_data[3] + splitoffset
	CopperMove $0ec, bp lsr 16
	CopperMove $0ee, bp & $ffff
	bp.l = *b\_data[4] + splitoffset
	CopperMove $0f0, bp lsr 16
	CopperMove $0f2, bp & $ffff

else

	;If we're not displaying over the bottom edge, just wipe out our copper commands
	CopperMove $1fe, 0
	CopperMove $1fe, 0
	CopperMove $1fe, 0

	CopperMove $1fe, 0
	CopperMove $1fe, 0
	CopperMove $1fe, 0

	CopperMove $1fe, 0
	CopperMove $1fe, 0    
	CopperMove $1fe, 0     

	CopperMove $1fe, 0
	CopperMove $1fe, 0    
	CopperMove $1fe, 0                                                                             

endif
I've deliberately used a display that ends before line 256 of the display because (although perfectly possible) complications are added if you're doing copper commands past the 256 line (also, it was helpful to have enough space to fit a large panel at the bottom of the screen)

Blitting itself adds complications, the best solution I could come up with for blitting near the edge of the bitmap was:

Code:
wrappedy = y mod #BufferHeight
yoverscan = (wrappedy + 32) - #BufferHeight
if yoverscan > 0 
	ClipBlit frame, x, wrappedy
	ClipBlit frame, x, -32 + yoverscan
else
	Blit frame, x, wrappedy
endif
Note that:
- Here, "x" is the real world value of the object - even if it's outside of the width bounds of the bitmap, due to the corkscrew effect it'll correctly blit to the right place, which is why I haven't used mod on the value (unlike the "y")
- Because I've increased the xclip value to the maximum possible word length, the ClipBlit function will correctly handle blitting over the right side edge.


I also used interleaved bitmaps (covered in my demos pack), and I borrowed a few tricks from other developers for redrawing the display.

Essentially, I didn't use the QBlit or BBlit commands, every time I drew an enemy I simply did the standard Blit command and "flagged" the underlying tile as dirty and needed to be redrawn from the tileset.

The simple way to do this would be to have a two dimensional array of X * Y tiles and loop through the lot to see if a tile is flagged as dirty. But there's a far faster way to handle it than to simply loop through each X and Y value to check if it's dirty (and thus needs redrawing). You simply have a one dimensional array of words corresponding to each row, and flag the bit corresponding to the column value. That way, if an entire row doesn't need to be redrawn, you can skip checking it entirely. Example

Code:
;This sets the X/Y tile as dirty
BlocksDirty(y) = BlocksDirty(y) BITSET x

;This checks to see if there's ANY dirty tiles on that entire row
if BlocksDirty(y)
	;Finally, we can loop through the X values
	for x = 0 to 15
		if BlocksDirty(y) BITTST x
			;redraw this block if we reach here
		endif
	next
endif
earok is offline  
Old 27 December 2018, 14:08   #2
Tigerskunk
Inviyya Dude!
 
Tigerskunk's Avatar
 
Join Date: Sep 2016
Location: Amiga Island
Posts: 2,770
Thanks for explaining, Earok...

Always nice to read how other devs do their thing...
Tigerskunk is online now  
Old 27 December 2018, 15:11   #3
E-Penguin
Banana
 
E-Penguin's Avatar
 
Join Date: Jul 2016
Location: Darmstadt
Posts: 1,213
Nice, thanks!

For dirty object management...

A thing I did in Father Christmas vs Dinosaurs was to maintain a list of objects needing updates. You just push objects as you find them and iterate the list once at the end - no looping through arrays checking an "is dirty" flag. Use KillItem to remove the things as you go through doing the updates.

In my case I had an array of all possible dinosaurs, and a list of pointers maintaining which ones are active.
E-Penguin is offline  
Old 27 December 2018, 15:27   #4
E-Penguin
Banana
 
E-Penguin's Avatar
 
Join Date: Jul 2016
Location: Darmstadt
Posts: 1,213
Quote:
Originally Posted by earok View Post
Code:
wrappedCameraY = CameraY mod #BufferHeight
DisplayBitMap #CopList_Main,FrontBuffer,CameraX,wrappedCameraY
splitline = #BufferHeight - wrappedCameraY
CopperReset #CopList_Main, 0
Is #BufferHeight a power of 2? If so you can replace the mod with a bitwise and. I think mod is one of the slowest arithmetic instructions on the 68000 as there's no hardware division unit.

Spiral scroll I will never understand though.
E-Penguin is offline  
Old 27 December 2018, 21:57   #5
earok
Registered User
 
Join Date: Dec 2013
Location: Auckland
Posts: 3,539
Quote:
Originally Posted by E-Penguin View Post
Nice, thanks!

For dirty object management...

A thing I did in Father Christmas vs Dinosaurs was to maintain a list of objects needing updates. You just push objects as you find them and iterate the list once at the end - no looping through arrays checking an "is dirty" flag. Use KillItem to remove the things as you go through doing the updates.

In my case I had an array of all possible dinosaurs, and a list of pointers maintaining which ones are active.
I kind of did something similar for handling the enemies, but for the screen it's like.. multiple blits will take place in one area if enemies are overlapping etc. I don't want to push to a list if there's already a list item flagging an X/Y coordinate as dirty, and I certainly don't want to redraw the same area twice. The dirty list also overlaps with the scrolling system, so when I scroll upwards for instance I flag the entire top row as dirty by simply setting the top value in the array to -1.

But there's likely ways to optimise what I did.

The modulus thing is interesting.. no, the buffer sizes aren't powers of 2, and I'd increase ram consumption a fair bit to make them power of 2 unfortunately (from memory the width of the bitmap is 384, the height is 320).
earok is offline  
Old 27 December 2018, 22:57   #6
Shatterhand
Warhasneverbeensomuchfun
 
Shatterhand's Avatar
 
Join Date: Jun 2001
Location: Rio de Janeiro / Brazil
Age: 41
Posts: 3,450
If you blit everything at the end of the frame and do no CPU operations inbetween you are wasting CPU cycles.

I usually try to blit between CPU operations. Execute AI code for the object, then blit, then move to next one, excute AI code, etc.

From what people told me, and I also have seen, when you ask the blitter to blit something, the program continue its flow up until it finds another blitter request, regardless if the Blitter is still doing its stuff or not. If the next blitter request happens while the blitter is still busy, program flow will stop and wait until blitter is available.

So if you just blit a lot of stuff at the end of the frame, CPU will be hanging waiting until all the blitter stuff is done.

I am pretty sure Blitter experts can use this to have very optimized use of the blitter. But it seems to be a good idea to have instructions between each blit you do.
Shatterhand is offline  
Old 27 December 2018, 23:25   #7
mcgeezer
Registered User
 
Join Date: Oct 2017
Location: Sunderland, England
Posts: 2,702
I'm no BB coder but I guess some concepts are the same.

Reading Earok's routines there mine differ slightly in asm but you might be able to put them in BB.

It's best to keep a third pristine copy of the screen in memory with no sprites for a fast rebuild of the screen, with this whenever you plot a sprite you simply store its offset from the top of the screen then when it comes to redrawing you just restore the screen pointer, add the offset then copy the tile block from the pristine screen.
mcgeezer is offline  
Old 27 December 2018, 23:57   #8
earok
Registered User
 
Join Date: Dec 2013
Location: Auckland
Posts: 3,539
Hope this all makes sense

@shatterhand To be clear, I'm not blitting everything at the end of the frame. I blit the dirty tiles first, and then the do the enemy logic (which includes both blitting and movement etc). Since the blits are interleaved, it means that the CPU doesn't have to wait for each of the bitplanes to be blitted before triggering the blit of the next blitplane.

But I haven't experimented with blitnasty to see if that helps things along, to be honest I'm not sure if it's necessary or how to properly use it.

@mcgeezer having a triple buffer has been my standard in the past (or using dual playfield where you can just simply clear the bobs on the top layer), but I haven't needed it with this - I simply need to blit the tile twice (front and back buffer) rather than three times with the third buffer, and when I redraw I simply redraw the original tile rather than blit from the third buffer.
earok is offline  
Old 28 December 2018, 00:48   #9
E-Penguin
Banana
 
E-Penguin's Avatar
 
Join Date: Jul 2016
Location: Darmstadt
Posts: 1,213
Both approaches are equivalent - blitting from an unmodified source, be it a bitmap or tile. As long as you have a quick way to determine which tile is used per coordinate.

I was thinking further about the mod. It's one of my favourite instructions but boy is it slow. It occurred to me that you could fit a look up table in 300 or so bytes (320 mod anything > 1 would fit in a .byte), using the y position as the array index. Perhaps not worth it for one mod per frame but if you're doing it a lot, maybe something to think about.
E-Penguin is offline  
Old 28 December 2018, 01:07   #10
earok
Registered User
 
Join Date: Dec 2013
Location: Auckland
Posts: 3,539
Quote:
Originally Posted by E-Penguin View Post
Both approaches are equivalent - blitting from an unmodified source, be it a bitmap or tile. As long as you have a quick way to determine which tile is used per coordinate.

I was thinking further about the mod. It's one of my favourite instructions but boy is it slow. It occurred to me that you could fit a look up table in 300 or so bytes (320 mod anything > 1 would fit in a .byte), using the y position as the array index. Perhaps not worth it for one mod per frame but if you're doing it a lot, maybe something to think about.
A look up table! Of course :O

I'm using mod a fair bit for different things, and I didn't realise how slow it was, so I'll definitely look at something like that on the next pass. Cheers.

I guess something like this could work

output = lookuptable(input & $1ff)

Which would work for any sized input, so long as the table held the results for 512 values.


The approaches are a bit different in that you're saving memory (two image buffers instead of three) and blitting (when you're scrolling, you only need to blit the new tile to two buffers instead of three). But there's advantages to the three buffer approach too.
earok is offline  
Old 28 December 2018, 02:07   #11
Shatterhand
Warhasneverbeensomuchfun
 
Shatterhand's Avatar
 
Join Date: Jun 2001
Location: Rio de Janeiro / Brazil
Age: 41
Posts: 3,450
Quote:
Originally Posted by earok View Post
Hope this all makes sense

@shatterhand To be clear, I'm not blitting everything at the end of the frame. I blit the dirty tiles first, and then the do the enemy logic (which includes both blitting and movement etc). Since the blits are interleaved, it means that the CPU doesn't have to wait for each of the bitplanes to be blitted before triggering the blit of the next blitplane.
I was more talking about E-Penguin said about the dirty object management, but from your explanation I see I may have even misunderstood what he said on first place, hehe
Shatterhand is offline  
Old 28 December 2018, 13:37   #12
Aladin
Registered User
 
Join Date: Nov 2016
Location: France
Posts: 854
why did you choose to zoom in on the amiga version? The screen is bigger on the other versions. This is for technical or aesthetic reasons?Thank you for this game, the animation of the character is good
Aladin is offline  
Old 28 December 2018, 20:34   #13
earok
Registered User
 
Join Date: Dec 2013
Location: Auckland
Posts: 3,539
Hi @Aladin, yes the resolution has been cut for performance reasons.

The original game was something like 640x480. While the AGA Amiga is perfectly capable of that resolution, it'd be slower due to how much would need to be blitted during scrolling and gameplay, also I'd need to burn up much more chipram for the buffers.

On top of that, to get the vertical resolution, I'd need to put it in interlace mode. Yuck.

To be honest, I think the game is actually better in the smaller screen display. There's not as much stuff going on at once, the action is much more tightly compressed around the player (rather than constantly having vehicles coming at you from far away from the player)
earok is offline  
Old 22 February 2019, 11:53   #14
MickGyver
Registered User
 
MickGyver's Avatar
 
Join Date: Oct 2008
Location: Finland
Posts: 643
Quote:
Originally Posted by earok View Post
Just some rough notes about how I made the underlying engine. Happy to answer any questions.
Very interesting and I have a question!

As a first step in creating something similar I'm trying to just achieve a copper split at a specific position (rolling screen). I'm doing something wrong I guess, the screen is garbled. This is what it looks like before "scrolling":


And this is what it looks like when I have scrolled a little (wrappedCameraY > 0):


Any idea what I have missed or done wrong earok?

Relevant code:

Code:
BitMap 0,320,240,5 
InitCopList 0,44,208,$15,8,32,0    ; y=44, lores, smoothscroll, 5 bitplanes, 8
CreateDisplay 0
DisplayPalette 0,0  
Use Bitmap 0

wrappedCameraY=0
splitoffset=0
splitline=0

Repeat
  VWait 1
  DisplayBitMap 0,0,0,wrappedCameraY
  splitline = 240 - wrappedCameraY
  CopperReset 0, 0

  ; Get the pointer to the front buffer bitmap
  *b.bitmap = Addr Bitmap(0)
  *b\_xclip = $7FFF
  
  ; Are we displaying over the bottom edge
  If splitline < 208
    ; Asume x-position at 0 for now
    splitoffset = 0
    ; Wait until the bottom edge       
    CopperWait 0, splitline + 44         
    ; Reset the display to the top of the bitmap
    ; .. Same as in earoks post above
  Else
    ; If we're not displaying over the bottom edge, just wipe out our copper commands
    ; ... Same as in earoks post above 
  EndIf
  
  
  If RawStatus(#KEY_DOWN)
    wrappedCameraY+1
    If wrappedCameraY>#SCR_H Then wrappedCameraY=0
  EndIf

Until RawStatus($45)=-1

Last edited by MickGyver; 22 February 2019 at 12:03.
MickGyver is offline  
Old 22 February 2019, 12:00   #15
earok
Registered User
 
Join Date: Dec 2013
Location: Auckland
Posts: 3,539
Quote:
Originally Posted by MickGyver View Post
Very interesting and I have a question!

As a first step in creating something similar I'm trying to just achieve a copper split at a specific position (rolling screen). I'm doing something wrong I guess, the screen is garbled. This is what it looks like before "scrolling":

And this is what it looks like when I have scrolled a little (wrappedCameraY > 0)

Any idea what I have missed or done wrong earok?

Relevant code:

Code:
BitMap 0,320,240,5 
InitCopList 0,44,208,$15,8,32,0    ; y=44, lores, smoothscroll, 5 bitplanes, 8
CreateDisplay 0
DisplayPalette 0,0  
Use Bitmap 0

wrappedCameraY=0
splitoffset=0
splitline=0

Repeat
  VWait 1
  DisplayBitMap 0,0,0,wrappedCameraY
  splitline = 240 - wrappedCameraY
  CopperReset 0, 0

  ; Get the pointer to the front buffer bitmap
  *b.bitmap = Addr Bitmap(0)
  *b\_xclip = $7FFF
  
  ; Are we displaying over the bottom edge
  If splitline < 208
    ; Asume x-position at 0 for now
    splitoffset = 0
    ; Wait until the bottom edge       
    CopperWait 0, splitline + 44         
    ; Reset the display to the top of the bitmap
    ; .. Same as in earoks post above
  Else
    ; If we're not displaying over the bottom edge, just wipe out our copper commands
    ; ... Same as in earoks post above 
  EndIf
  
  
  If RawStatus(#KEY_DOWN)
    wrappedCameraY+1
    If wrappedCameraY>#SCR_H Then wrappedCameraY=0
  EndIf

Until RawStatus($45)=-1
It's kind of hard to say, vertical split screen scrolling is super fragile and will break with the smallest thing wrong. I'll take a look at running your code tomorrow and tweaking it until it works (this is my monthly Amiga project weekend, so a good time to work on things like that)
earok is offline  
Old 22 February 2019, 12:07   #16
MickGyver
Registered User
 
MickGyver's Avatar
 
Join Date: Oct 2008
Location: Finland
Posts: 643
Quote:
Originally Posted by earok View Post
It's kind of hard to say, vertical split screen scrolling is super fragile and will break with the smallest thing wrong. I'll take a look at running your code tomorrow and tweaking it until it works (this is my monthly Amiga project weekend, so a good time to work on things like that)
Cheers! I forgot to add the pictures of what it looks like earlier, so added those. I'm linking the source and necessary files, that is probably a quicker way for you to see what's wrong.

EDIT: Hopefully I will figure it out before you put any time into it.
Attached Files
File Type: zip ScrollY.zip (31.8 KB, 105 views)
MickGyver is offline  
Old 22 February 2019, 12:18   #17
earok
Registered User
 
Join Date: Dec 2013
Location: Auckland
Posts: 3,539
Looking at the screenshots, it seems the copper commands extend past line 255 without properly being handled. But looking at the code, it seems everything is done before line 255. So I'm not quite certain. When I'm next at my laptop, I'll take a look if you haven't solved it already.
earok is offline  
Old 22 February 2019, 13:45   #18
MickGyver
Registered User
 
MickGyver's Avatar
 
Join Date: Oct 2008
Location: Finland
Posts: 643
Quote:
Originally Posted by earok View Post
Looking at the screenshots, it seems the copper commands extend past line 255 without properly being handled. But looking at the code, it seems everything is done before line 255. So I'm not quite certain. When I'm next at my laptop, I'll take a look if you haven't solved it already.
I was on the same track and I think I actually figured it out. I added this to the end of the copper commands:
Code:
CopperWait 0, 208 + 44
CopperEnd
Is that the correct way yo handle it?
(By the way if you want, you can answer me in the "Tilemap Scrolling Demos" thread, I don't want to hijack your Kiwi's Tale dev thread)
MickGyver is offline  
 


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools

Similar Threads
Thread Thread Starter Forum Replies Last Post
The Kiwi's Tale - Full length platformer for CD32 earok project.Amiga Game Factory 82 17 April 2019 23:26
Stickies - Sticky notes BippyM support.WinUAE 1 06 August 2013 22:57
Work In Progress notes - WinUAE (v1.0) Carlos Ace News 11 09 May 2005 11:08

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +2. The time now is 21:15.

Top

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.
Page generated in 0.11162 seconds with 16 queries