Python 中的 CreateConsoleScreenBuffer

CreateConsoleScreenBuffer in Python

我在控制台中用 Python 到 运行 编写了一个 3D 游戏。为了阻止它闪烁,我必须将我想要显示的内容写入 ConsoleScreenBuffer。文档是 this。我知道我必须使用:

import win32console
buffer = win32console.CreateConsoleScreenBuffer()

但是 CreateConsoleScreenBuffer() 的参数是什么?在文档中它说:

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

那是 C 语言。help(win32console.CreateConsoleScreenBuffer) 没有提供有用的信息。前两个参数是 int,第二个是“PySECURITY_ATTRIBUTES”对象,第三个也是 int。 (我觉得?)

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

我没有找到任何在线实施的示例。

如果您知道任何其他加快控制台绘制速度的方法,请告知。

这是我的(不是喷气机)游戏。它闪烁,因为它绘制得不够快。我遵循的教程是用 c++ 编写的,并使用 CreateConsoleScreenBuffer 来消除闪烁,因为有了它,所有内容都是一次绘制的,而不是 先后

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

我假设您正在学习 javid/OLC 的控制台 FPS 教程。

简单的答案是:您需要 win32conwin32console,下面是更好的答案。

有点晚了,但是这个页面:win32console docs @ Tim Golden and PyConsoleScreenBuffer Object @ Tim Golden 对解决这个问题非常有用。有两种方法可以做到这一点。一种是使用 win32console and win32con,另一种是使用 ANSI Escape Sequences。实际上,您要做的就是将光标 to/print 移动到控制台上的 0,0 处。这是方法一:

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![=10=]", 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.

这在您的控制台 FPS 中应该可以正常工作(使用 WriteConsoleOutputCharacter)。只需将命令放在 javid 放置它们的地方,这应该可以正常工作。第二种方法是使用colorama/ANSI 序列将光标return 到home 位置。这种方法可以总结为:os.system("echo 3[0;0H")。您不能使用 print 函数使用转义序列,它们不受支持(使用 colorama 除外)。这是一个例子:

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 3[0;0H") # return cursor to home position, ready for next frame.

希望对您有所帮助! - 天空