DinosaurMoritz
DinosaurMoritz

Reputation: 145

CreateConsoleScreenBuffer in Python

I wrote a 3D game with Python to run in a console. To stop it from flickering, I have to write what I want to display to a ConsoleScreenBuffer. The documentation is this. I know I have to use:

import win32console
buffer = win32console.CreateConsoleScreenBuffer()

but what are the parameters for the CreateConsoleScreenBuffer()? In the documentation it says:

HANDLE WINAPI CreateConsoleScreenBuffer(
  _In_             DWORD                dwDesiredAccess,
  _In_             DWORD                dwShareMode,
  _In_opt_   const SECURITY_ATTRIBUTES *lpSecurityAttributes,
  _In_             DWORD                dwFlags,
  _Reserved_       LPVOID               lpScreenBufferData
);

That's in C. help(win32console.CreateConsoleScreenBuffer) does not give useful information. The first 2 parameters are ints, the second a "PySECURITY_ATTRIBUTES"-object an the third is also an int. (I think?)

CreateConsoleScreenBuffer(DesiredAccess, ShareMode, SecurityAttributes, Flags)
DesiredAccess=GENERIC_READ and GENERIC_WRITE : int
GENERIC_READ and/or GENERIC_WRITE
ShareMode=FILE_SHARE_READ and FILE_SHARE_WRITE : int
FILE_SHARE_READ and/or FILE_SHARE_WRITE
SecurityAttributes=None : PySECURITY_ATTRIBUTES
Specifies security descriptor and inheritance for handle
Flags=CONSOLE_TEXTMODE_BUFFER : int
CONSOLE_TEXTMODE_BUFFER is currently only valid flag

I didn't find any examples of this being implemented online.

If you know any other methods of making a console draw faster, please tell.

This is my (not jet) game. It flickers, because its not drawn quick enough. The tutorial I was following is in c++ and uses CreateConsoleScreenBuffer to eliminate the flickering, because with it everything is drawn at once and not successively.

import os
import time
import math
import threading


hardMap = [
    ["#","#","#","#","#","#","#","#","#","#","#","#","#","#","#","#"],
    ["#",".",".",".",".",".",".",".",".",".",".",".","#",".",".","#"],
    ["#",".",".",".",".",".",".",".",".",".",".",".","#",".",".","#"],
    ["#",".",".",".",".",".",".",".",".",".",".",".","#",".",".","#"],
    ["#",".",".",".",".",".",".",".",".",".",".",".","#",".",".","#"],
    ["#",".",".",".",".",".",".",".",".",".",".",".","#",".",".","#"],
    ["#",".",".",".",".",".",".",".",".",".",".",".","#",".",".","#"],
    ["#",".",".",".",".",".",".",".",".",".",".",".",".",".",".","#"],
    ["#",".",".",".",".",".",".",".",".",".",".",".",".",".",".","#"],
    ["#",".",".",".",".",".",".",".",".",".",".",".",".",".",".","#"],
    ["#",".",".",".",".",".",".",".",".",".",".",".",".",".",".","#"],
    ["#",".",".",".",".",".",".",".",".",".",".",".",".",".",".","#"],
    ["#",".",".",".",".",".",".",".",".",".",".",".",".",".",".","#"],
    ["#","#",".",".",".",".",".",".",".",".",".",".",".",".",".","#"],
    ["#","#","#",".",".",".",".",".",".",".",".",".",".",".","#","#"],
        ["#","#","#","#","#","#","#","#","#","#","#","#","#","#","#","#"],

      ]


gameMap = "".join(["".join(hardMap[n]) for n in range(len(hardMap))])

notDone = True

rotationSpeed = 0.02

playerX = 5
playerY = 5
playerA = 0

fov = 4
fov = math.pi / fov

depth = 20

fps = 30

screenWidth = 120
screenHeight = 40
os.system(f'mode con: cols={screenWidth} lines={screenHeight}')

screen = [" " for n in range(screenHeight * screenWidth)]
mapWidth = len(hardMap[0])
mapHeight = len(hardMap)



def printScreen(string):
    os.system('cls')
    time.sleep(0.01)
    for x in [string[i:i+screenWidth] for i in range(0,len(string),screenWidth)]:
        print("".join(x))
    
c = 0
while(notDone):
    c += 1
    playerA = playerA + rotationSpeed
    startTime = time.time()
    
    for x in range(screenWidth):
        rayAngle = (playerA - fov / 2) + ((x / screenWidth) * fov)
        distanceToWall = 0
        hitWall = False
        shade = " "
        
        eyeX = math.sin(rayAngle)
        eyeY = math.cos(rayAngle)

        """
        with open("log.txt","a") as f:
            f.write("x"+str(eyeX)+"\n")
            f.write("y"+str(eyeY)+"\n")
            f.write("h"+str(mapHeight)+"\n")
            f.write("w"+str(mapWidth)+"\n")
            f.write("\n")
        """
        
        while not hitWall and distanceToWall < depth:
            distanceToWall = distanceToWall + 0.1
 
            testX = int(playerX + eyeX * distanceToWall)
            testY = int(playerY + eyeY * distanceToWall)

            if testX < 0 or testX >= mapWidth or testY < 0 or testY>= mapHeight:
                hitWall = True
                distanceToWall = depth
                 
            elif gameMap[testY * mapWidth + testX] == "#":    
                    hitWall = True
                
                
        ceiling = int((screenHeight / 2.0) - (screenHeight / distanceToWall))
        floor = screenHeight - ceiling  

        for y in range(screenHeight):

    
            

            
            if y < ceiling:
                screen[y * ceiling + x] = " "
            elif y > ceiling and y <= floor:
                
                if distanceToWall <= depth / 4: shade = u"\u2588"
                elif distanceToWall < depth / 3: shade = u"\u2593"
                elif distanceToWall < depth / 2: shade = u"\u2592"
                elif distanceToWall < depth / 1: shade = u"\u2591"
                else: shade = " "
        
                screen[y * screenWidth + x] = shade
                
            else:

                b = 1.0 - ((y - screenHeight / 2.0) / (screenHeight/2))

                if b < 0.25: shade = "#"
                elif b < 0.5: shade = "X"
                elif b < 0.75: shade = "."
                elif b < 0.9: shade = "-"
                else: shade = " "
                
                screen[y * screenWidth + x] = shade
                
    printScreen(screen)
    """
    with open("log.txt","a") as f:
            f.write(str("\n".join(screen)))
            f.write("\n\n\n\n\n\n\n\n")
    """
    time.sleep(max(1./fps - (time.time() - startTime), 0))

Upvotes: 1

Views: 2017

Answers (1)

Blue Sky
Blue Sky

Reputation: 78

I am assuming you are following javid/OLC's Console FPS tutorial.

The simple answer is: you need win32con along with win32console, a better answer is below.

A little late but this page: win32console docs @ Tim Golden and PyConsoleScreenBuffer Object @ Tim Golden were very useful to figuring this out. There are two ways of doing this. One is using win32console and win32con and the other is to use ANSI Escape Sequences. Effectively, all you are trying to do is move the cursor to/print at 0,0 on the console. Here is method one:

import win32console, win32con, time
myConsole = win32console.CreateConsoleScreenBuffer(DesiredAccess = win32con.GENERIC_READ | win32con.GENERIC_WRITE, ShareMode=0, SecurityAttributes=None, Flags=1) # create screen buffer
myConsole.SetConsoleActiveScreenBuffer() # set this buffer to be active
myConsole.WriteConsole("Hello World!") # Effectively the print func.
myConsole.WriteConsoleOutputCharacter(Characters="Hello World!\0", WriteCoord=win32console.PyCOORDType(5,5)) # Print at coordinates

time.sleep(3) # this must be called or you won't see results. This is because after running the code (because there is no loop) the cmd.exe takes the console over.

This should work fine in your console FPS (using WriteConsoleOutputCharacter). Just place the commands where javid places them and this should work fine. The second method is to use colorama/ANSI Sequences to return the cursor to the home position. This method can be summed up in: os.system("echo \033[0;0H"). You cannot use the escape sequences using the print function, they are not supported (except using colorama). Here is an example:

from colorama import init
from os import system
init() # I bother with this because I use colors too. Echo prints slower than print, so don't use echo if you can avoid it.

# ... game
# ... generate string to print
print(frame) # or just use you method of printing each line.
system("echo \033[0;0H") # return cursor to home position, ready for next frame.

I hope this helps! - Sky

Upvotes: 3

Related Questions