With the display working, it's time to have some blitting fun. How about the good old classic bouncing ball?
We have a background of random dots, and a round ball, with black outline, bouncing around the screen on top of it, passing outside the screen edges. Those details are all important, for example we don't want to only be able to handle rectangular sprites, or to crash when a sprite is partially outside the screen. So how do we do that?
We need two functions, which I have called "blit" and "erase". Let's start with "erase", because it's simpler: it erases a given area of the screen, overwriting it with the background image we have saved. It's simple enough:
def erase(self, x, y, background, size=8):
buffer = self.buffer
width = self.width
if not -size < x < width:
return
if x < 0:
size += x
x = 0
if x > width - size:
size = width - x
page = y // 8
if 0 <= page <= 7:
index = x + width * page
buffer[index:index + size] = background[index:index + size]
page += 1
if 0 <= page <= 7 and y % 8:
index = x + width * page
buffer[index:index + size] = background[index:index + size]
First we save the class attributes to local variables, just to gain a little bit of speed and avoid one lookup. Then we check if we should even bother displaying anything — if it's out of bounds, just return. Then we check the case when it's just at the edge — we simply adjust the size and position of the area then, to only handle the part that fits on the screen. Finally we copy the relevant portions of the background to the two pages of buffer.
Oh, I forgot to mention, we only handle sprites that are 8 pixels high, but any number of pixels wide. We can make larger sprites by stacking several on top of each other, if we need, and we can have smaller ones by manipulating the mask, as I will explain later on.
Great, we have a way of deleting anything we have drawn on the screen now, which is important for animation. But we still don't have a way of drawing anything. How do we do that? With the blit method!
The blit method takes some data, with an optional mask, and copies them to the specified coordinates of the screen. More specifically, the bits provided as mask are AND-end with the pixels already in the buffer, and then the bits given as data are XOR-ed with it. That lets us make some empty space with the mask first, and then put the white pixels in there. We could have OR-ed the white pixels instead, but with XOR we can have more interesting effects when we don't use the mask. The code follows:
def blit(self, x, y, data, mask=b''):
buffer = self.buffer
width = self.width
size = max(len(data), len(mask))
if not -size < x < width:
return
if x < 0:
data = data[-x:]
mask = mask[-x:]
size += x
x = 0
if x > width - size:
data = data[:width - size - x]
mask = mask[:width - size - x]
size = width - x
page = y // 8
shift = y % 8
if 0 <= page <= 7:
index = x + width * page
for byte in mask:
buffer[index] &= ~(byte << shift)
index += 1
index = x + width * page
for byte in data:
buffer[index] ^= byte << shift
index += 1
page += 1
if 0 <= page <= 7 and shift:
shift = 8 - shift
index = x + width * page
for byte in mask:
buffer[index] &= ~(byte >> shift)
index += 1
index = x + width * page
for byte in data:
buffer[index] ^= byte >> shift
index += 1
First we do the same kind of magic for handling screen edges as before. Then we apply the mask and the data to the two possible pages that they can touch, bit-rotated appropriately. Unfortunately we can't use the slice notation here, which is a great shame, because it would be faster.
And here is a simple program that tests this:
for x in range(1280): display.pixel(random.randint(0, 127), random.randint(0, 63), 1) box = b'\x00<fZZf<\x00' mask = b'~\xff\xff\xff\xff\xff\xff~' x = 0 y = 0 dx = 1 dy = 1 background = bytearray(display.buffer) while True: display.blit(x, y, box, mask) display.show() display.erase(x, y, background) x += dx y += dy if not -12 <= x < 128+4: dx = -dx if not -12 <= y <= 64+4: dy = - dy
And the effect:
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.
no pewpew but some progress with stage and the ugame universe? Sometimes moving is already a nice thing ;)
Are you sure? yes | no
things seems to move fast, nice job!
Are you sure? yes | no
incidentally, that is completely useless for running PewPew on it — so it's not movement forward
Are you sure? yes | no