I made this little game to test if my Nokia 5110 screen can handle games. As in handle many frames per second/game loops.
I am using a library made for this screen called Adafruit_Nokia_LCD
These are the imports for some context, if needed:
import Adafruit_Nokia_LCD as LCD import Adafruit_GPIO.SPI as SPI import threading from PIL import Image from PIL import ImageDraw from PIL import ImageFont
This is how I load images. I have some black and white images, use a binary converter to turn them into a multiline string. These are some tamagotchi sprites for example:
tam1 = """00000000000000111100000 00001111000000111110000 00001111100001111110000 00011111100001111110000 00011111111111111111000 00011111111111111111000 00011111111111111111000 00111111111111111111000 00111111111111111111100 01001111000000011111110 01100000011110000000010 10110000111101000000001 10110000111110100000001 10110000111110100000001 10110000111110100000001 01110000011100100000001 01100000001111000000010 00100101000000000000010 00010011000000000000100 00001100000000000011110 00000011111111111100001 00000100000000000000001 00001000000000000011110 00001001000000000010000 00000111000000000010000 00000001000000000011100 00000010000000000100010 00000100000111111000010 00000100011000000111100 00000011100000000000000"""
There are two more sprites, for a walking animation called tam2 and tam3
I split them into lines:
tam1a = tam1.split('\n') tam2a = tam2.split('\n') tam3a = tam3.split('\n')
I have a different thread running to capture key input, in which case is spacebar to jump:
key = "lol" #uhh this is the keyword for no input pretty much idk i just left it in getch = _Getch() def thread1(): global key lock = threading.Lock() while True: with lock: key = getch() #this is some code that works just like the msvcrt version threading.Thread(target = thread1).start()
This is the game loop, after setting some variables:
#jump variables dist = 0 #distance off the ground gup = False #isjumping #obstacle variables xx = 0 #x position of the obstacle that loops through the screen #other variables score = 0; ind = 0; #current frame of player, there is 3 extraspeed = 0; #makes the pillar go faster as your score rises while True: #gameloop start draw.rectangle((0,0,LCD.LCDWIDTH,LCD.LCDHEIGHT), outline=255, fill=255) #clears the screen --^ draw.text((0,0),str(score),font=font) #draw score extraspeed = floor(score / 100) #set extraspeed based on score if extraspeed > 10: extraspeed = 10 score += 3 draw.rectangle((xx,32,xx+3,42),outline=0,fill=0) xx = xx + 4 +extraspeed #move the pillar if xx >= 84: xx = -4; if key == ' ': if dist == 0: gup = True key = "lol" #if dist = 0 means its on the ground #the "lol" thing is something I left over for some reason, it means no key pressed else: key = "lol" if gup == True: if dist != 12: #jumps up to 12 pixels off the ground dist += 3 else: gup = False #start falling if reached top else: if dist > 0: dist -= 3 else: dist = 0 #ind is the current animation sprite to draw if ind == 1: i = 12-dist #top left corner of drawn sprite y j = 60 #top left corner of drawn sprite x for line in tam1a: for c in line: if c == '1': draw.point((j,i),fill=0) j+=1 i+=1 j=60 #make same as j at start if ind == 2: i = 12-dist #top left corner of drawn sprite y j = 60 #top left corner of drawn sprite x for line in tam2a: for c in line: if c == '1': draw.point((j,i),fill=0) j+=1 i+=1 j=60 #make same as j at start if ind == 3: i = 12-dist #top left corner of drawn sprite y j = 60 #top left corner of drawn sprite x for line in tam3a: for c in line: if c == '1': draw.point((j,i),fill=0) j+=1 i+=1 j=60 #make same as j at start ind += 1 if ind == 4: ind = 1 #restart the animation draw.line((0,43,83,43),fill=0) draw.line((0,44,83,44),fill=0) #draw some ground if xx >= float(67) and xx<= float(80) and dist <= 7: break #Some simple collision detection # Display image. disp.image(image) #draw everything disp.display() #display everything time.sleep(0.2) #5 frames per second draw.text((40,10),'Hit',font=font) #got here if 'break' occurs disp.image(image) #displays hit, which means game over and loop is broken disp.display()
First of all: This screen is connected through GPIO pins to my Raspberry-Pi.
What do you think of this method to load sprites?
How about drawing the sprites? I iterate through the string, line by line then character by character and draw the pixels respectively (0 means dont draw, 1 means draw black)
Now while this code works, I experience ghosting and image burn in my lcd screen. While I know the nokia 5110 screen wasn't designed to be for a gaming system, can my code be optimized to a point of reducing this ghosting ?
What do you think of my key input getting method, is a seperate thread fine for this job?
Lastly, would a different drawing method allow me to reach 60 frames per second or is it just impossible due to the way this screen is manufactured?
Here's a pic. Notice how there is image burn from the last frame? There is only one pillar, and only one frame of the player, but two appear. Is this fixable, or is it because of the screen only?