Moving sprites

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

Moving sprites

Bobber
So I've got a pretty smooth running function for drawing a sprite, and when I include it in a while loop it works well... until it interacts with another sprite. The method I have will completely overwrite a word. which is great when I want it to do that, but I dont want it erasing everything. I'm just curious should I start over on this, or would it be relatively easy to put a helper function inside of my drawSprite to modify the value in poke as needed?

method void drawSprite(Array sprite, int x, int y){
                var int i, init, step, rShift, lShift, temp,memAddr;
                let step = Main.mod(x, 16);
                let lShift = Main.pow(2, (16-step));
                let rShift = Main.pow(2, step);
                let i = 0;
                let memAddr = 16384+(32*y)+(x/16);
                while(i<16){
                        let init = sprite[i];
                        do Memory.poke((memAddr+(i*32)),(init*rShift));
                        if(~(lShift=0)){
                                do Memory.poke((memAddr+(i*32)+1),(init/lShift));
                        }
                        let i = i + 1;
                }
                return;
        }
Sprite is an array holding the value for that row with the sprite in an unshifted position.
I thought about peaking at the existing value then adding init value properly shifted to it, but that ended up looking really trashy. The only approach that keeps popping into my head is use an alpha layer, but when my template is only 2 colors I cant figure that out either
Reply | Threaded
Open this post in threaded view
|

Re: Moving sprites

Bobber
Well if anyone was curious I found a.... solution-ish.
method void drawSprite(Array sprite, int x, int y, boolean first){
                var int i, init, step, rShift, lShift, temp,memAddr;
                let step = Main.mod(x, 16);
                let lShift = Main.pow(2, (16-step));
                let rShift = Main.pow(2, step);
                let i = 0;
                let memAddr = 16384+(32*y)+(x/16);
                while(i<16){
                        let init = sprite[i];
                        let temp = Memory.peek((memAddr+(i*32)));
                        if(first){
                                do Memory.poke((memAddr+(i*32)),(init*rShift));
                        } else {
                                do Memory.poke((memAddr+(i*32)),(temp+(init*rShift)));
                        }
                        if(~(lShift=0)){
                                let temp = Memory.peek((memAddr+(i*32)+1));
                                do Memory.poke((memAddr+(i*32)+1),(init/lShift));
                        }
                        do Output.moveCursor(12,0);
                        do Output.printInt(step);
                        do Sys.wait(5);
                        let i = i + 1;
                }
                return;
        }

each frame if it is the first sprite(closest to origin) drawn then it sets that value, otherwise it adds its value to the one already existing there.

still getting some flicker, and it doesn't like when rShift value = 15, and after about 3 rows back and forth it gets weird. I guess I should include the loop I'm using to test it too:

while(true) {
        while(i<11){
                let first = false;
                if(i=0){
                        let first = true;
                }
                let enemy = enemies[i];
                //the getFrame function alternates between 1 of 2 sprite arrays each time it's called
                do gameArea.drawSprite(enemy.getFrame(),enemy.getX(),enemy.getY(),first);
                               
                //edge detection
                if(((enemy.getX()=240)&(Alien.getDirection()=1))
                | ((enemy.getX()=0)&(Alien.getDirection()=-1))){
                        let j = 0;
                        do Alien.changeDirection();
                        while(j<11){
                                let first = false;
                                if(j=0){
                                        let first = true;
                                }
                                let enemy = enemies[j];
                                do gameArea.drawSprite(clear,enemy.getX(),enemy.getY(),first);
                                do enemy.incY(16);
                                let j=j+1;
                        }
                       
                } else {
                        do enemy.incX(Alien.getDirection()); //increase x by direction(1 or -1)
                }
                let i = i + 1;
        }
        let i = 0;
}
Reply | Threaded
Open this post in threaded view
|

Re: Moving sprites

ivant
The "standard" method to displaying sprites with holes is to add a bit-mask to the sprite. For example, if you have part of the sprite (I'll just use 8 bits here, but it's the same for 16) 00110100 and you want to only show the middle 6 bits, the bit-mask for this would be 01111110.

You can then display this part of the sprite by reading the current value from the video memory in a variable pixels, then using bit-wise operations: pixels = (pixels & ~(mask)) | (sprite & mask) and then store this variable back in the video RAM.

The "standard" Jack compiler does not optimize the code. You should keep in mind the restrictions of the machine if you want to write fast code. For example, you compute memAddr + (i*32) several times in the same loop. This is quite inefficient. Instead you should use an additional variable to compute it once and store it there. Better yet, update the memAddr itself:

let i = i + 1;
let memAddr = memAddr + 32;

This way you remove the multiplication, which is a relatively expensive operation.

You can also precompute all the values for lShift and rShift and store them in a static array.

I suppose the moveCursor / printInt is for debugging purposes, right?
Reply | Threaded
Open this post in threaded view
|

Re: Moving sprites

Bobber
Yeah it's for debugging. I did the multiplication because it was easier for me to reason about each iteration through the loop. Now I'm sure it does what I want I can just remove that and do a memAddr+=32 at the end of the loop. I'm also thinking since this was built for a 16 bit wide sprite but the sprites are at most 12 pixels wide I can make this work without the masking. I should be able to move it 1 pixel per frame without the pixels I care about interfering.  That feels like it would be simpler and more efficient than the mask method you described. At least in this case, but to seem to know more about this than i, and all advice is appreciated