This is an old revision of the document!
Table of Contents
Pygame Terminal II
- A more advanced version of Pygame Terminal
About
In Pygame Terminal we discussed a very basic kind of game terminal. This can be used to create many kinds of games such as the Robots game.
However, we may like to increase the functionality of this framework by adding a few quality of life features.
Pygame Terminal Major Changes
The first major change is in the Game class draw loop. The first part of the Game class now looks like this:
import pygame
class Game:
def __init__(self, window):
self.window = window
self.screen = window.screen
self.logo = window.logo
self.font = window.font
# Game Data
self.FPS = 60
self.clock = pygame.time.Clock()
def start(self):
while True:
self.checkEvents()
self.update()
self.draw()
self.clock.tick(self.FPS)
def draw(self):
self.screen.fill((0, 0, 0)) # Clear the screen
self.drawText(0, 2, "it works!", "gray")
pygame.display.flip()
def update(self):
pass
def checkEvents(self):
cmd = ""
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.quit()
return
if event.type == pygame.KEYDOWN:
# key down event, process keys.
if event.key == pygame.K_q:
self.quit()
def drawText(self, at_x, at_y, text, color):
text_surface = self.font.render(text, False, color)
x = self.window.fontwidth * at_x
y = self.window.fontheight * at_y
self.screen.blit(text_surface, (x + 2, y))
def quit(self):
quit()
exit()
As you can see we have moved to using self.clock.tick(self.FPS), and have split the check events and update loops. We will also add a quit function. The quit function will have more importance later, such as if we need to save game state when exiting.
update() is important because, until now, we have been responding to the user's input as it happens, and we have been 'tagging along' mob movement and game system updates to this directly (as in the robots game). However, to be more strict we should keep these in an update() function which updates the game state even if the player is not typing anything. This allows for more freedom in how the game could be designed, such as in the javascript Shooty Ships game.
Game state and update()
Since we are going to have a game state that will be update()'d every frame, there needs to be a standard way of representing the state of the screen. Normally this is done by drawText()'ing whatever you need, and it just works. However, this is a simplistic approach which will hamstring our ability to fully develop our terminal-esque simulation. What we want to do next is follow the path of VT development in the past and move from a screen towards a terminal. Thus, the terminal class is born.
First let's add and discuss the class, then we will integrate it into the Game class.
Class Character
You will first need a simple Character class for Class Terminal:
Character.py
class Character:
def __init__(self, ch = ' ', color = 'gray'):
self.ch = ch
self.color = color
This just allows us to move color data to a per-color basis. Later we can add a background color or define ANSI colors. We will save that for Pygame Terminal III.
Class Terminal
from Character import Character
import threading
class Terminal:
def __init__(self, cols, rows):
self.cols = cols
self.rows = rows
self.cx=0
self.cy=0
self.interval = 0.535 # 535ms
self.cc=False
self.repeat_timer()
self.buf = [[0 for x in range(cols)] for y in range(rows)]
for y in range(rows):
for x in range(cols):
self.buf[y][x] = Character()
def setch(self, x, y, ch, color):
self.buf[y][x].ch = ch
self.buf[y][x].color = color
def putch(self, ch, color):
self.buf[self.cy][self.cx].ch = ch
self.buf[self.cy][self.cx].color = color
self.cx = self.cx + 1
if self.cx >= (self.cols-1):
self.cx = 0
self.cy = self.cy + 1
if self.cy >= (self.rows-1):
print("scroll screen")
def delch(self):
self.cx = self.cx - 1
if self.cx < 0:
self.cx = 0
self.buf[self.cy][self.cx].ch = ' '
self.buf[self.cy][self.cx].color = 'gray'
# Function to schedule the timer to repeat
def repeat_timer(self):
self.cc = not self.cc
#print(f"Cursor state: {'On' if self.cc else 'Off'}")
if self.interval > 0.1:
threading.Timer(self.interval, self.repeat_timer).start()
