News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_ervin

character-based tile engine with parallax scrolling

Started by ervin, 03:41, 01 August 12

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

MacDeath

Quote
QuoteAlso, I've attached the source code to the first post in this thread.
  Sorry I wasn't looking there!
haha, good old hacker can't wait the release and go hack their way the hard way.

fano

Very nice job ! i love tiles based rendering  ;)
Didn't have time to look more at the source code but i have some questions , how do store and draw your tiles , there are optimisations or it is linear ?
"NOP" is the perfect program : short , fast and (known) bug free

Follow Easter Egg products on Facebook !

AMSDOS

Quote from: ervin on 12:39, 03 August 12
Interesting... I'm not actually explicitly using INC SP anywhere in my code, but I'm using quite a lot of self-modifying code so I'd imagine that disassembling it could be inaccurate?


From what I can work out, it isn't being applied in the tile engine sequence. It's doing a lot of 'PUSH AF'..'INC SP' during the setup stages of the program and where the Border is meant to go, though once it's done all that there are no more 'PUSH AF'..'INC SP'. Though I noticed that large 'PUSH DE' in the Disassembly (along with your source) and perhaps 1 or 2 'PUSH HL'..'POP HL' sequences.



QuoteCan you give me an example of what you changed? I'm fascinated to know if there is any merit in studying the effects of replacing the INC SP's with POP AF.


From the Binary file (Tiles.bin), I replaced 'INC SP' (opcode 33) with 'POP AF' (opcode F1), though to really improve the code it's better to have an Assembly Source cause the only advantage I see in it is your saving a few bytes here and there cause for every "PUSH AF", to return the Stack Pointer back to where it was you need 2 "INC SP", and cause your program is using 3x"PUSH AF" in some places, it needs 6x"INC SP" to counteract that. If "POP AF" is being used, only 3 are required and you save 3 bytes. Cause that little sequence was doing it so many times, I think there's about 20 bytes extra taking up room. Seems silly to go to the hassle.  :D
* Using the old Amstrad Languages :D   * with the Firmware :P
* I also like to problem solve code in BASIC :)   * And type-in Type-Ins! :D

Home Computing Weekly Programs
Popular Computing Weekly Programs
Your Computer Programs
Updated Other Program Links on Profile Page (Update April 16/15 phew!)
Programs for Turbo Pascal 3

ervin

Quote from: CP/M User on 10:44, 04 August 12
From what I can work out, it isn't being applied in the tile engine sequence. It's doing a lot of 'PUSH AF'..'INC SP' during the setup stages of the program and where the Border is meant to go, though once it's done all that there are no more 'PUSH AF'..'INC SP'. Though I noticed that large 'PUSH DE' in the Disassembly (along with your source) and perhaps 1 or 2 'PUSH HL'..'POP HL' sequences.

From the Binary file (Tiles.bin), I replaced 'INC SP' (opcode 33) with 'POP AF' (opcode F1), though to really improve the code it's better to have an Assembly Source cause the only advantage I see in it is your saving a few bytes here and there cause for every "PUSH AF", to return the Stack Pointer back to where it was you need 2 "INC SP", and cause your program is using 3x"PUSH AF" in some places, it needs 6x"INC SP" to counteract that. If "POP AF" is being used, only 3 are required and you save 3 bytes. Cause that little sequence was doing it so many times, I think there's about 20 bytes extra taking up room. Seems silly to go to the hassle.  :D

Ah yes, I had a look through the asm file produced by ccz80, and the INC SP's are all done during border, locate etc. commands.
It's probably something to do with the way the compiler passes parameters around.

The large PUSH DE sequence is how I clear a 1000-byte buffer each frame. I love that technique - it's really quick.
I first encountered the technique when I was reading about how David Webb (I think that is his name) wrote Starion, and how he cleared the screen before drawing the next screen update. Of course, it's a well known technique, but it was new to me, and when I tried it I was astonished by how quickly it cleared a large area of memory!

Other PUSH/POP pairs are unfortunately unavoidable instances where HL and DE need to be tucked away safely for a brief moment before returning to their former glory in order to correctly continue a loop.

ervin

Quote from: fano on 19:48, 03 August 12
Very nice job ! i love tiles based rendering  ;)
Didn't have time to look more at the source code but i have some questions , how do store and draw your tiles , there are optimisations or it is linear ?

Thanks fano.

Before I explain how I draw the tiles, I need to explain what happens each frame.

Their are 2 1000-byte buffers.
One is for the previous frame, and the other is for the current frame.
Each byte represent one character cell, and can contain any 8-bit number (which is why my tile references are currently 8-bit).

Anyway, both buffers are looped through together in each frame, and if they both contain the same value in the same cell, I ignore the cell. If they contain different values, I print a tile to that cell. This is where most of the speed comes from.

Alrighty, on to the data structures...

I have a number of 16-byte tile arrays, labelled a0 to a169.
There is a table called tblTile, which contains 16-bit addresses pointing to these tile arrays.

There are a number of objects (like PACLAND_HOUSE, SUN etc.) which contain 8-bit values. These are offsets that tell the program which element to look at inside tblTile.
For example, PACLAND_HOUSE has this as its first line of tile references:
005,006,007,007,007,007,007,007,007,007,007,007,007,008,009,010,

This means: use the 6th tile referenced in tblTile (0-based, so 005 is the 6th element), then use the 7th tile, then use the 8th tile several times, then use the 9th tile etc.

I also have a number of arrays for which layers objects belong in, what their X and Y positions are, and I also store each object's width, height and some other info.

Each frame calculates which object is within a certain distance of the viewport, and whether or not that object should be ignored for the current frame.
Any object that is not ignored has its position stored in a temporary variable, which is then adjusted in relation to the camera's position, and further adjusted based on which layer it is in.
This bit is quite messy and I actually can't remember how it works! I should have put more comments in!  :-[

Clipping is then worked out, and the required tile offsets are put into the 1000-byte buffer that will be printed to the screen.
The buffer is processed as described above near the start of this post.

Now, I have a problem with the tiles. I can only have 255 tiles at the moment, which is a really bad limitation.
I've thought of having another table containing a "tileBank" value for each cell, but this is a bit messy and would slow things down a bit.

Ideally, I should not have tblTile at all, and have the objects simply storing the 16-bit addresses of the tiles they require.
Unfortunately, all the objects would then take twice as much memory, and so would the 2 1000-byte buffers.
Also, the loop to compare the current and previous buffers would slow down as well.

However, the printing of tiles would actually become faster, as I would be able to remove the tblTile lookup code.
So I need to run a number of tests based on how many tiles need to be printed each frame, and see if removing tblTile would result in a faster engine overall, despite having slower buffer cell comparisons.

I'll report back after I have some more useful info.

MacDeath

Do you have some scripts or application to easily enter any tileset and/or tilemapping into the code ?


Would be nice so guys who can't code but can into pixeling could play and test with this engine.

ervin

I have got a very basic program which reads a bitmap and then creates data statements for me, but it's really messy and needs more work (some manual work is required to turn the resulting data into something useful within the ccz80 program). Also, I'm going to try changing the tile references to 16-bit, and this will cause the tile application to need some changes (but it will also simplify it).

When I've got something usable, I'll upload it.

MacDeath

65536 tiles ?

Or perhaps you will use some of those extra bits to differenciate different sort of "tiles", like adding colision or transparency ink/mask or indicatingthe layer to which it would apply ? (just asking)


The multiscroll/paralax is meant to give some sort of deepness so I guess each layer actually would never use some same common tiles.

ervin

If there was enough memory, certainly you could have 65536 tiles, but of course RAM will run out long before that number is reached.

It's up to the artist whether or not tiles would ever be shared among the different layers.
Certainly blocks of solid colour could be shared.

Anyway, I've just finished converting the program to use 16-bit for tile references, and I've been able to remove the tile table lookup entirely. That has resulted in faster tile rendering.

Unfortunately, 16-bit means that looping through the front/back buffer data is slower, as I have to increment each buffers' pointer twice now for each iteration. In fact, the program now runs around 25 to 30% slower.  :(
It seems like a lot, but... not only do the buffers take longer to step through now, but so do the comparisons between back/front buffer cells, as I have to check both high and low bytes now. Bummer.

I'll have a go at adding "tile sets" to the 8-bit version now. That might be a fix for the 255 tile issue, without such severe slowdown (I hope)!

Actually a "tileSet" table may not be a bad idea. It can be stepped through along with the front buffer, and I could use 4 bits for the set number (meaning I'd have up to 16 tile sets of 255 tiles each), and the other 4 bits could be used as flags to indicate thing like "platform", "deadly", etc.

Or I could have up to 8 sets of tiles (by using 3 bits) and therefore have 5 flag bits for tile types.
8 tile sets would allow for 2040 tiles, and would take (2040 * 16) = 32640 bytes.
Of course, RAM would be used up before that number of tiles was reached, so yes, I will limit the number of tile sets to 8.

MacDeath

#34
Yes, I knew 65K tiles was not realistic, but it was fun to suggest it anyway...
what a nightmare to draw all those Pixel art tiles anyway.


on the other hand 256 tiles is not that small actually.


Many games don't use more than that per level.




Quote8 tile sets would allow for 2040 tiles, and would take (2040 * 16) = 32640 bytes.
clearly more than enough.


perhaps another solution would be to keep those tiles in 8bit, but just use like 2 or 3 tilesets, that would be used by some layers especially.
Some layers may share a common tileset, others not.


Just the place of those tilesets in RAM would indicate what layer it goes with/for... not sure if coding would work that way.
Many method can do the same result, but not exactly with the same CPU/RAM ratio I guess.


From what I learned when trying my hands at some JimPowers tiles, 256 is a bit limited, but 512 tiles is quite enough.
But yeay, gotta keep some place for sprites too.


You can also try with some 16x16 (mode1) or 8x16 (mode0) sized tiles but as you do a character based engine, this may also be quite a complication too.


Back to the engine.
It would be nice to have options to set the size of the whole tilemap (level design) and also to have some function to get a few layers (or even all) to cycle endlessly...
Like you know, meteors or clouds...


what is the current size of the tilemap ? (X and Y axis)


To limit it vertically or horizontally may gain a few things... I mean most paralax are by essence limited to horizontal or vertical only, just the other axis is somewhat limited (or not).


See Shadow of the beast, when you are "outside",  the vertical scrolling is quite limited, if even turned on...

arnoldemu

Could you use a dirty buffer?
With 1 bit per tile?
You should know if the tile is dirty.. so you can mark it. No need for comparison..?
You could then also discard 8 tiles at the same time.

Also, you could mark an entire row as dirty or not discarding whole lines.

this may help.. it may not, depends on your design.
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

ervin

Quote from: arnoldemu on 08:44, 06 August 12
Could you use a dirty buffer?
With 1 bit per tile?
You should know if the tile is dirty.. so you can mark it. No need for comparison..?
You could then also discard 8 tiles at the same time.

Also, you could mark an entire row as dirty or not discarding whole lines.

this may help.. it may not, depends on your design.

Hmmm, that's an interesting idea.

The engine design revolves around current/previous buffer tile comparisons, but maybe I could put an intermediate step in there based on the dirty buffer idea.
My other program - Chunky Pixel Collision - relies on a dirty buffer for speeding up the display (amongst other things!), but that game design has large areas of emptiness on the screen, so it makes sense in that case.
This tile engine doesn't, but the number of comparisons could be reduced by checking a dirty buffer first... I'll try a few things out.

Thanks.

AMSDOS


I had a look at your source, though my C is so rusty only bits and pieces stick out. :(


I'm interested in the Parallax Scrolling you've used cause you've got different parts of the image moving around different, unfortunately I wasn't sure which segments deal with the scrolling.


Have you seen David Hall's Space Storm II (ACU titled it: "Meteor Storm II")?
This 10-Liner features some amazing parallax scrolling moving a line at a time (in a small window). The routine I knocked up in the "Silly" thread is how I've dealt with generating a line by line effect, though it applies it in a physical way, so basically everything gets shifted down. I've gone into David's program numerous times and have been able to isolate where his Parallax Scrolling is, though his approach alludes me, and what he seems to have done is make a way to scroll the Stars and scroll the Rocks separately, and keep everything (e.g. his Alien Character) in it's current place.
I'm amazed that something so small can have me stumped. 8o
* Using the old Amstrad Languages :D   * with the Firmware :P
* I also like to problem solve code in BASIC :)   * And type-in Type-Ins! :D

Home Computing Weekly Programs
Popular Computing Weekly Programs
Your Computer Programs
Updated Other Program Links on Profile Page (Update April 16/15 phew!)
Programs for Turbo Pascal 3

ervin

I haven't seen Meteor Storm II.
Sounds interesting!
I'll be hunting it down shortly...

Alrighty, this is (sort of) how the scrolling works.
- there are 4 layers: wallpaper, background, playfield and foreground.
- objects are placed within any of these 4 layers (and of course objects are made up of tiles).
- the layer determines the speed that something scrolls at.
- wallpaper doesn't scroll.
- background scrolls at half speed (i.e. 1 character square of movement every two frames).
- playfield scrolls at 1 character square per frame.
- foreground scrolls at 2 character squares per frame.

All that I do is determine (based on the above rules) where in the view window an object should be printed.
To be honest, I don't remember how it works!
I *think* it's something like this:
- wallpaper object's position is adjusted based on half the distance between it and a reference point.
position
- playfield object's position is adjusted based on the distance between it and a reference.
- foreground objects position is adjusted based on double the distance.

Something like that anyway!

AMSDOS


Quote from: ervin on 00:36, 13 August 12
I haven't seen Meteor Storm II.
Sounds interesting!
I'll be hunting it down shortly...


I've attached the BASIC program on here before, but I'll attach the Disk Image cause it's a bit of a tricky program to track down all ACU 10-Liners were put into Disk Images. The bulk of this 3k game has been written in Assembly, though there is some BASIC interaction, and while the game isn't always completable, it's got that one more go factor about it and I've managed to complete it (without cheating).


Since I last wrote, I've managed to isolate one of the Scrolling Routines from this game which scrolls the stars down the screen, but it's all quite tricky, as I said earlier though it's a credit to David Hall to have been able to incorporate a Scrolling technique which deals with a certain object and specifically move it without worrying about anything else on screen being moved. Unfortunately the complexity of the Disassembly eludes me. I'll post the relevant code in here, so I hope someone can help interpret the routine.

QuoteAlrighty, this is (sort of) how the scrolling works.
- there are 4 layers: wallpaper, background, playfield and foreground.
- objects are placed within any of these 4 layers (and of course objects are made up of tiles).
- the layer determines the speed that something scrolls at.
- wallpaper doesn't scroll.
- background scrolls at half speed (i.e. 1 character square of movement every two frames).
- playfield scrolls at 1 character square per frame.
- foreground scrolls at 2 character squares per frame.

All that I do is determine (based on the above rules) where in the view window an object should be printed.
To be honest, I don't remember how it works!
I *think* it's something like this:
- wallpaper object's position is adjusted based on half the distance between it and a reference point.
position
- playfield object's position is adjusted based on the distance between it and a reference.
- foreground objects position is adjusted based on double the distance.

Something like that anyway!


Seems to be a credit to you to be able to generate several things happening at once. I've never seen so many Variables & arrays, it reminds me of Codies Wizard Willy, which has all this stuff in the foreground and a slower moving background, the border thing you have happening there is also like the effect generated in Wizard Willy with the yellow status graphic which sits around the rest of the screen. Only difference is WW doesn't Scroll Up or Down, though you can move from Left to Right and Right to Left back to the Beginning of the Level.



* Using the old Amstrad Languages :D   * with the Firmware :P
* I also like to problem solve code in BASIC :)   * And type-in Type-Ins! :D

Home Computing Weekly Programs
Popular Computing Weekly Programs
Your Computer Programs
Updated Other Program Links on Profile Page (Update April 16/15 phew!)
Programs for Turbo Pascal 3

ervin

Wow - Space Storm II is very impressive for a type-in!
Nice and smooth.

Yeah, I do use a lot of variables. There are a lot of things going on in the background, and I tried to reduce the number of variables, but that would generally result in more complicated code, so I decided to keep 'em.
:)

Gryzor

Wow, that is lovely, though very hard! Impressive scrolling...

Axelay

Quote from: CP/M User on 07:15, 13 August 12

Since I last wrote, I've managed to isolate one of the Scrolling Routines from this game which scrolls the stars down the screen, but it's all quite tricky, as I said earlier though it's a credit to David Hall to have been able to incorporate a Scrolling technique which deals with a certain object and specifically move it without worrying about anything else on screen being moved. Unfortunately the complexity of the Disassembly eludes me. I'll post the relevant code in here, so I hope someone can help interpret the routine.



Maybe the problem is thinking of it as scrolling?  ;)  It's all "sprites".  There's a main loop between &2730 and &275c.  At the beginning it makes a series of calls.  Call &275e moves the meteors down a line, Call &27f6 moves the stars down a line, and Call &2861 handles the player.  Call &275e is there twice in the loop, so there's your parallax effect.  You can see they are sprites by breaking from the program, putting junk text on the screen and typing Call &275e.


[EDIT]


Just had a quick look at the routine at &275e, it looks like the meteors are governed by a table starting at &3e81, taking the form of a y co-ordinate and then a screen address for the top left hand corner of the sprite. The value at &3e80 is used as a counter to the next meteor.  Broadly speaking, the first thing it does at &275e is check the counter to the next meteor, and the code that follows is for creating a new meteor if the counter has reached &1A.  From &2785 the routine does it's main loop of printing meteors.  This first checks for meteors that dont exist (y=0) or need clipping at the top or bottom, but if it gets to &27a8 it is going to print a whole meteor.

AMSDOS

#43
Quote from: Axelay on 11:45, 13 August 12

Maybe the problem is thinking of it as scrolling?  ;)  It's all "sprites".  There's a main loop between &2730 and &275c.  At the beginning it makes a series of calls.  Call &275e moves the meteors down a line, Call &27f6 moves the stars down a line, and Call &2861 handles the player.  Call &275e is there twice in the loop, so there's your parallax effect.  You can see they are sprites by breaking from the program, putting junk text on the screen and typing Call &275e.


[EDIT]


Just had a quick look at the routine at &275e, it looks like the meteors are governed by a table starting at &3e81, taking the form of a y co-ordinate and then a screen address for the top left hand corner of the sprite. The value at &3e80 is used as a counter to the next meteor.  Broadly speaking, the first thing it does at &275e is check the counter to the next meteor, and the code that follows is for creating a new meteor if the counter has reached &1A.  From &2785 the routine does it's main loop of printing meteors.  This first checks for meteors that dont exist (y=0) or need clipping at the top or bottom, but if it gets to &27a8 it is going to print a whole meteor.


:laugh:  yeah maybe the problem is "it's not scrolling", magazines like ACU & AA were using the term Parallax Scrolling to describe David Hall's (Space Storm II & Space Storm 3) games.


I've been looking at the bit of code which displays & moves the stars around at &27F6 which isn't quite a complicated, this is what I extract from it. I hope I don't offend anyone or David Hall. I was going to start up a new thread, but since I opened the can of worms in here I'll post it here, sorry Ervin.


      org &2000


      ld hl,&3a98      ;; This is storing something here and moves down to
      inc (hl)      ;; &3AFB
      ld a,(hl)
      cp &06
      jr nz,movestars
.genstar   ld (hl),&00      ;; this section must deal with randomly drawing a star
      ld hl,&3a96      ;; to screen
      ld de,&0003     
.makemore   add hl,de      ;; I'm guessing this will make more
      ld a,(hl)
      cp &00
      jr nz,makemore
      ld (hl),&01
      ld a,r         ;; The R Register can be used as a way of making something random
      ld b,a
.wasnocarry   xor a
.bisnotzero   inc a
      cp &2d
      jr nc,wasnocarry
      djnz bisnotzero
      inc hl
      ld (hl),a
      inc hl
      ld (hl),&c8
.movestars   ld b,&24
      ld ix,&3a99
.mainloop   ld a,(ix+&00)
      cp &00
      jr z,iszero
      ld h,(ix+&02)
      ld l,(ix+&01)
      ld a,(hl)
      cp &20
      jr nz,toscroll
      ld (hl),&00
.toscroll   call movestuff
      ld (ix+&02),h
      ld (ix+&01),l
      ld a,(hl)
      cp &00
      jr nz,notzero
      ld (hl),&20
.notzero   inc (ix+&00)
      ld a,(ix+&00)
      cp &c5
      jr nz,iszero
      ld (ix+&00),&00
      ld (hl),&00
.iszero      ld de,&0003
      add ix,de
      djnz mainloop
      ret


.movestuff   ld a,h
      add &08
      ld h,a
      cp &c0
      ret nc
      push de
      ld de,&c050
      add hl,de
      pop de
      ret



It takes some time to get going, I found when I call &2000 it didn't do anything, but if I put it into a loop


mode 1:for a=1 to 2000:call &bd19:call &2000:next a


that should get it going. Though I was hoping that bit of assembly could be explained cause I'm having trouble understanding it. :(
* Using the old Amstrad Languages :D   * with the Firmware :P
* I also like to problem solve code in BASIC :)   * And type-in Type-Ins! :D

Home Computing Weekly Programs
Popular Computing Weekly Programs
Your Computer Programs
Updated Other Program Links on Profile Page (Update April 16/15 phew!)
Programs for Turbo Pascal 3

Powered by SMFPacks Menu Editor Mod