8
\$\begingroup\$

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()

My problem:


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?

Image burn and ghosting

200_success
145k22 gold badges190 silver badges478 bronze badges
asked Oct 23, 2018 at 21:09
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Although I don't have too much hardware knowledge at this level, I suspect you may be trying to update the screen too frequently. As I recall from using screens like this long ago, large redraws would definitely not be instantaneous (and may cause some wiping effects). I suspect the issue is that you are clearing the entire screen (which takes time) and then subsequently trying to set certain pixels. It might be more efficient to keep track of the delta of pixels changed between frames and only update those. \$\endgroup\$ Commented Oct 29, 2018 at 5:36

1 Answer 1

1
\$\begingroup\$

I noticed that you are updating extraspeed every frame.

An alternative to this could be:

if extraspeed < 10:
 desired_boost = floor(score / 100)
 if desired_boost <= 10:
 extraspeed = desired_boost
 else:
 extraspeed = 10

This way, you aren't updating extraspeed every frame, only when it is less than 10.

Also, you should remove the semicolons on the following lines:

score = 0;
ind = 0; #current frame of player, there is 3
extraspeed = 0;

I'll look further into this for you, but I've never personally drawn to a screen other than my computer screen so I may be unable to assist you further.

answered Oct 30, 2018 at 0:38
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.