Jump to content

Recommended Posts

You did it!! Awesome ! My greetings, I was sure that the TI was able to move 512 bytes of vram in a frame !

And with this configuration you can freely use all sprites (no sprite cloning glitch)

 

Now what is missing is a nice pc tool to design tiles and levels....

(if you are interested we could port some of the algorithms I have in matlab to do the optimizations matching colors and extracting meta data, to Magellan)

 

Thanks. And I'm sorry your Uridium game didn't win - it's the most impressive use of the 9918A I have seen.

 

I haven't actually done the color matching by hand, so I already have some code in Magellan (the method is called writeArtragFile :)). My algorithm is not perfect, though, so I have to give it a few hints about which tiles to match. I would be very interested in a description of how your algorithm works. The problem with releasing this as a general Magellan feature is that it's so specialized that it will be difficult to explain how to use it, but it would help if the color matching was perfect.

Hi this is the core of my matching algorithm. All tiles common to the upper and lower bank are dealt apart, the following script deals only with tiles that differ in order to reduce the computational load.

Matlab deals with array and matrices of double natively, I will try to explain all the main steps with comments in the code.
About the input:
- uniqueTilesu is a matrix Nux64 of double where Nu is the # of tiles in the upper bank and 64 is a bitmap representation of the 8x8 tile
- uniqueTilesd is a matrix Ndx64 of double where Nx is the # of tiles in the lower bank etc etc
- MAP is the color palette: on TMS it is fixed, it is used only in the conversion from bitmap to TMS vram data (done from another script outside this function). Just neglect it.

function [chru,chrd,clrud,nTu,nTd] = jointoptimalvdpencode (uniqueTilesu,uniqueTilesd,MAP);

[chu,clu] = vdpencode (uniqueTilesu,MAP); % convert each column vector of 64 bytes from bitmap to TMS representation chu and clu are respectively shapes and colors, with size Nu*8
[chd,cld] = vdpencode (uniqueTilesd,MAP); % same for tiles in the lower bank

% convert shapes and colors in matrices 8xNu and 8xNd of uint8 (they were arrays of doubles)

chu = uint8(reshape(chu,8,size(uniqueTilesu,1)));
chd = uint8(reshape(chd,8,size(uniqueTilesd,1)));

clu = uint8(reshape(clu,8,size(uniqueTilesu,1)));
cld = uint8(reshape(cld,8,size(uniqueTilesd,1)));

% transpose bitmap representations of tiles into matrices of uint8 with sizes 64xNu and 64xNd respectively
Tu = uint8(uniqueTilesu');
Td = uint8(uniqueTilesd');

[s,i] = sort(sum(chu)); % sort the shapes in increasing order according to the sum of the shapes within each tile 
% the step is not needed but filters at the very beginning of the array tiles with all zeros in the shape
% it could speed up the matching (maybe), - skip this step safely

chu = chu(:,i);	% apply the same sorting computed above to all (shapes, colors and bitmap version)
clu = clu(:,i);
Tu = Tu(:,i);

i = find(s);	% report indexes of shapes with sum not zero
for k=i			% cycle only on tiles with at least one 1 in the shape
    for j=1:8	% convert line by line color 0 in color 1 -> they look the same but can cause mismatches 
        if bitand(clu(j,k),15) == 0
            clu(j,k) = bitand(clu(j,k),240) + 1;
        elseif bitand(clu(j,k),240) == 0
            clu(j,k) = bitand(clu(j,k),15) + 16;
        end
    end
end

[s,i] = sort(sum(chd));		% same as above for tiles in the lower bank
chd = chd(:,i);
cld = cld(:,i);
Td = Td(:,i);

i = find(s);
for k=i
    for j=1:8
        if bitand(cld(j,k),15) == 0
            cld(j,k) = bitand(cld(j,k),240) + 1;
        elseif bitand(cld(j,k),240) == 0
            cld(j,k) = bitand(cld(j,k),15) + 16;
        end
    end
end

setmax = 1:size(chu,2);	       % set of the indexes for tiles in the upper bank. It is the set {1,2,3,..,Nu}
setmin = 1:size(chd,2);        % same for the lower bank: it is just the set {1,2,3,..,Nd}

k = 1;
PCT  = uint8(zeros(8,256));
PGTu = uint8(zeros(8,256));
PGTd = uint8(zeros(8,256));
nTu = uint8(zeros(64,256));
nTd = uint8(zeros(64,256));

for x = 1:size(chu,2)		% try to fit each tile from the upper bank with any tile with the lower bank
    for y = setmin
        % see above for a description of match_colors
        [t cu cd] = match_colors(chu(:,x),clu(:,x),chd(:,y),cld(:,y));

        if ~isempty(t)
            setmin = setdiff(setmin ,y);	% if they match exclude the two matching tiles from further comparisons and put the "result" from match_colors in the new tileset
            setmax = setdiff(setmax ,x);
            PCT(:,k) = t;
            PGTu(:,k) = cu;
            PGTd(:,k) = cd;
            nTu(:,k) = Tu(:,x);
            nTd(:,k) = Td(:,y);
            k = k+1;
            break;
        end
    end
end

for x = setmax			% if there are tiles from the upper bank non matching with any other, put them in the end of the new tileset
    PCT(:,k)  = clu(:,x);
    PGTu(:,k) = chu(:,x);
    PGTd(:,k) = [ 129,66,36,24,24,36,66,129 ];	% blank with a cross the shape of lower bank corresponding to non matching tiles
    nTu(:,k) = Tu(:,x);
    k = k+1;
end
for y = setmin		% if there are tiles from the lower bank non matching with any other, put them in the end of the new tileset
    PCT(:,k)  = cld(:,y);
    PGTd(:,k) = chd(:,y);
    PGTu(:,k) = [ 129,66,36,24,24,36,66,129 ];	% blank with a cross the shape of upper bank corresponding to non matching tiles
    nTd(:,k) = Td(:,y);
    k = k+1;
end
        
nTu = nTu'; % transpose the result (needed from outer code - skip this step safely)
nTd = nTd';
PCT = PCT';
PGTu = PGTu';
PGTd = PGTd';

k=k-1;

% return only tiles actually used in the new tile set (i.e. from 1 to k)

chru = PGTu(1:k,;
chrd = PGTd(1:k,;
clrud = PCT(1:k,;

nTu = nTu(1:k,;
nTd = nTd(1:k,;

return


function [k,cu,cd] = match_colors(chu,clu,chd,cld)
  
% verify if tiles {chu,clu} and {chd,cld} can fit
% if they fit, it returns shapes in cu,cd and the common color k
% if they do not fit, color k is empty

% this code tries to flip bits in each line of shapes swapping the two colors in order to match color attributes
% moreover each time a given line has a single color, the code will consider that line compatible with all lines with at least one color equal to that color

 cu = chu;
 cd = chd;
 k  = clu;
 
 for i = 1:8
     
     cu(i) = chu(i);
     if	(chu(i) == 255) % this line is flat with all 1's
         t1 = uint8(uint8(0:15) + bitand(clu(i),240)); % array of all possible colors where the left color is given
         t2 = uint8(uint8(0:15)*16 + bitand(clu(i),240)/16); % array of all possible colors where the right color is given
     elseif (chu(i) == 0) % same as above if the line is flat with 0's
         t1 = uint8(uint8(0:15)*16 + bitand(clu(i),15));
         t2 = uint8(uint8(0:15) + bitand(clu(i),15)*16);
     else
         t1 = clu(i); % line not flat
         t2 = [];
     end
         
     cd(i) = chd(i); % some as above for tile in the lower bank
     if	(chd(i) == 255)
         s1 = uint8(uint8(0:15) + bitand(cld(i),240));
         s2 = uint8(uint8(0:15)*16 + bitand(cld(i),240)/16);
     elseif (chd(i) == 0)
         s1 = uint8(uint8(0:15)*16 + bitand(cld(i),15));
         s2 = uint8(uint8(0:15) + bitand(cld(i),15)*16);
     else
         s1 = cld(i);
         s2 = [];
     end
     
% here t1,t2,s1,s2 hold the set of colors compatible with the given line or to its bit wise negation

     m11 = intersect (t1,s1); % colors are compatible without negating shapes
     m12 = intersect (t1,s2); % colors are compatible negating shape of lower bank
     m21 = intersect (t2,s1); % colors are compatible negating shape of upper bank
     m22 = intersect (t2,s2); % colors are compatible negating both shapes (impossible, same as m11 - skip this case safely)
     
     if ~isempty(m11)
         k(i) = min(m11);
         
     elseif ~isempty(m12)
         k(i)  = min(m12);
         cd(i) = ~cd(i); % negate bits in shape of lower bank
         
     elseif ~isempty(m21)
         k(i)  = min(m21);
         cu(i) = ~cu(i); % negate bits in shape of upper bank
         
     elseif ~isempty(m22)
         k(i)  = min(m22);
         cu(i) = ~cu(i); % negate both shapes
         cd(i) = ~cd(i);
         
     else
         k = []; % no compatible colors
         return % if even one single line is incompatible abort matching on this couple of tiles
     end
 end
 
  
     
     
     
Edited by artrag
  • Like 2

ps

Doing a parallax, as each line of characters can scroll at its own speed, you do not need to update all 512 tiles each frame.

The farthest objects (top side of the screen) will be updated less frequently (e.g. once each 8 frames), while the closest objects (lower side of the screen) will be updated at each frame.

 

In a moon patrol like scene:

http://atariage.com/forums/topic/216921-season-7-round-3-matt-patrol/

far mountains moves once each 4 frames, closer hills twice each 4 frames, and lower ground at each frame.

 

Edited by artrag

ps

Doing a parallax, as each line of characters can scroll at its own speed, you do not need to update all 512 tiles each frame.

The farthest objects (top side of the screen) will be updated less frequently (e.g. once each 8 frames), while the closest objects (lower side of the screen) will be updated at each frame.

 

In a moon patrol like scene:

http://atariage.com/forums/topic/216921-season-7-round-3-matt-patrol/

far mountains moves once each 4 frames, closer hills twice each 4 frames, and lower ground at each frame.

 

Two questions:

 

1) Are you scrolling an entire field or just characters/tiles which have changes?

1a) Does it matter that a tile has changes or not?

2) Are those flying space bras?

 

Two questions:

 

1) Are you scrolling an entire field or just characters/tiles which have changes?

1a) Does it matter that a tile has changes or not?

2) Are those flying space bras?

 

#2 - If they are, get rid of 'em, let those babies fly free! :)

  • Like 1

ps

Doing a parallax, as each line of characters can scroll at its own speed, you do not need to update all 512 tiles each frame.

The farthest objects (top side of the screen) will be updated less frequently (e.g. once each 8 frames), while the closest objects (lower side of the screen) will be updated at each frame.

 

If my demo I have the 2 tile banks arranged like this:

 

Bank 0:

Bottom frame 0

Bottom frame 1

Bottom frame 2

Bottom frame 3

Top frame 0

Top frame 1

Top frame 4

Top frame 5

 

Bank 1:

Bottom frame 4

Bottom frame 5

Bottom frame 6

Bottom frame 7

Top frame 2

Top frame 3

Top frame 6

Top frame 7

This allows me to scroll the bottom twice as fast as the top. I would only have to update the top tiles every second frame if it wasn't for the fact that I'm also using double buffering for the tile/name/screen image table. Perhaps this is overkill for the demo, but if it took longer time to update the table it would be required to avoid flickering.

Ok I was overspeaking. I had forgotten the double buffering.

It is not trivial to do not update the whole tile name table for the slower lines (unless you do not duplicate the same tile definitions in the two pages, but this wastes VRAM)....

 

My proposal for a two speed scrolling (thinking to a Moon Patrol like game) could be:

 

Page 0

Top bank: frames 0,2,4,6 for far mountains

Bottom bank: frames 0,1,2,3 for closer scene

Page 1

Top bank: frames 1,3,5,7 for far mountains

Bottom bank: frames 4,5,6,7 for closer scene

In this way you can update only the lower bank (256 tiles) for 3 steps of scrolling out of 4, and update the whole screen (512 tiles) only at frame 0 and 8 of the lower scene (the fastest).
Using two pattern name tables you can spread the load of moving the extra 256 tiles on 4 steps (64 tiles at time) - but you already do this in Titanium IIRC.
[edit] My initial proposal was wrong, I think I fixed it now.
NB
Bottom bank is updated 4 times for each update of the upper bank: in pixels the speed of the upper bank 1/4 of the lower one.
You could also increase this ratio to 1/8 if you set the minimum step of two pixels in the lower bank
Edited by artrag
  • 3 weeks later...

Okay, I watched this on real iron today and was tickled a little. Now I have a personal request: Rasmus, if you can create a demo using your parallax technique of Shadow of the Beast title screen on the TI, I will literally wet myself...

 

http://www.youtube.com/watch?v=JLrRv_qWlp8

 

(The music is not the original, but a daXX remix, and not one of the better ones. But this was the only video I could find of the actual Amiga screen I am talking about.)

 

Here is the Commodore 64 conversion, which I am guessing is loading from tape since the action does not start until about 59 seconds in.

 

 

And for completeness, many conversions compared (the music on the Genesis conversion is a bit off):

 

  • Like 1

Okay, I watched this on real iron today and was tickled a little. Now I have a personal request: Rasmus, if you can create a demo using your parallax technique of Shadow of the Beast title screen on the TI, I will literally wet myself...

 

Hmm, it's one thing to scroll independent sections, but to scroll stacked layers on a tile based screen would require far to many characters, I think. The good news is that you will stay dry for the time being :).

  • Like 2

Comparing what the Amiga could do with its custom chips is a bit unfair to the TI due to its use of bitplanes. Jimmy Maher goes into great detail of how the Amiga achieved what it did in his Platform Studies book "The Future Was Here," and includes a Psygnosis game as an example.

 

Speaking of Psygnosis, their audio and visuals were top notch, but gameplay... certainly not pretty! Beast was one of those games back in the day that you showed friends and said "look what my Amiga can do," but you wouldn't let them play it since they'd very likely give up in frustration a few minutes after handing them the joystick. Even using a trainer, I had an incredibly tough time beating it.

  • Like 2

Comparing what the Amiga could do with its custom chips is a bit unfair to the TI due to its use of bitplanes. Jimmy Maher goes into great detail of how the Amiga achieved what it did in his Platform Studies book "The Future Was Here," and includes a Psygnosis game as an example.

 

Speaking of Psygnosis, their audio and visuals were top notch, but gameplay... certainly not pretty! Beast was one of those games back in the day that you showed friends and said "look what my Amiga can do," but you wouldn't let them play it since they'd very likely give up in frustration a few minutes after handing them the joystick. Even using a trainer, I had an incredibly tough time beating it.

 

Given, but look at the releases for 8-bit machines which lack blitters and multi-layer bit plane capabilities. The Amstrad and Commodore, in particular.

 

Shadow of the Beast was the whole reason I bought my first Amiga 500 -- and the damned thing did not come with the game! (Up until I bought that 500, I settled with the Commodore 64 version.) It did, however, have a few other games like Blood Money and Killing Game Show, so that sated me until I could get my hands on Beast. It took me a weekend, but I finally beat the game and still come back for more.

  • 8 months later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...