Python curses:来自文件的addstr()为该行的其余部分打印空白

时间:2014-01-01 00:46:36

标签: python curses ascii-art

我正在用Python编写一个非常简单的Python游戏。在这一点上,我成功地允许玩家(只是一个“@”字符)在一个窗口内移动。

我有几个带有ascii-art的文件,我打印到窗口作为填充玩家可以移动的世界的东西。例如,我有一个名为“house”的文件,其中包含:

 _ . ^ . _
/____.____\
|         |
| ## _ ## |
|_""_H_""_|

我有一个Thing课程如下:

class Thing(object):    

    def __init__(self, Xstart, Ystart, looksLike, list, window):
        self.Xstart = Xstart
    self.Ystart = Ystart
    self.X = Xstart
    self.Y = Ystart

    self.looksLike = looksLike

    self.boundries = []
    self.find_boundries()

    list.append(self)

    self.draw(window)


def find_boundries(self):

    file = open(self.looksLike).readlines()
    for line in file:
        for char in line:
        if char == '\n':
            pass
        elif char == ' ':   # skip all whitespace
            self.X += 1
        else:
                self.boundries.append([self.X, self.Y])
            self.X += 1

        self.Y += 1
        self.X = self.Xstart

    self.X = self.Xstart    # reset x & y to starting coordinates
    self.Y = self.Ystart


def draw(self, window):

    #file = open(self.looksLike).readlines()

    #for line in file:
    #    window.addstr(self.Y, self.X, line)
    #    self.Y += 1

#self.Y = self.Ystart

    file = open(self.looksLike).read()

    for char in file:
        window.addch(self.Y, self.X, char)
    if char == '\n':
            self.Y += 1
    self.X = self.Xstart
    else:
    self.X += 1

self.X = self.Xstart    
self.Y = self.Ystart

因此,我的Thing类的构造函数将文件名作为参数(LooksLike),draw方法打开文件,读取文件并将其内容打印到窗口。然后,我可以创建一个房屋对象,将我的“房子”文件作为参数传递,并将我的ascii房子打印到窗口。

问题是,一旦将对象打印到窗口,当我将播放器移动到打印对象的右侧时,播放器就会消失。然而,在打印物体的上方,下方和左侧,玩家保持在视野中。例如,

 _ . ^ . _
/____.____\
|         |
| ## _ ## |
|_""_H_""_|
                  @

在这个位置,“@”字符是可见的,但如果我向上移动一个空格,它就会消失。如果我继续向上移动玩家,“@”会在超出房子最顶端角色后重新出现。

我认为这个问题是由于addstr()和addch()(我已尝试过两者)的性质打印空白直到窗口结束,但我无法找到任何关于此的文档。

我考虑过为每个打印的对象创建一个新窗口,但是当窗口上打印了多个对象时,这似乎会非常麻烦。此外,我希望在打印到屏幕上的对象周围定义边框,这些边框不仅仅是正方形或矩形。

无论如何,从文件打印到没有尾随空格的窗口并且没有为每个打印对象创建新窗口?

2 个答案:

答案 0 :(得分:1)

很遗憾,我从未使用过诅咒,但我看不到你的玩家类。

然而,也许这个片段可能会给你一些想法(按'x'退出游戏)(使用WASD移动播放器)(需要启用ANSI的控制台:

#! /usr/bin/python3

class _GetchUnix:
    def __init__(self):
        import tty, sys

    def __call__(self):
        import sys, tty, termios
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return ch

getch = _GetchUnix ()

house = ''' _ . ^ . _ 
/____.____\\
|         |
| ## _ ## |
|_""_H_""_|'''

class Screen:
    def __init__ (self, width, height, background):
        self.width = width
        self.height = height
        self.bg = '\x1b[{}m'.format (40 + background)
        self.clear = '\x1b[0m'
        self.objects = []

    def __iadd__ (self, obj):
        self.objects.append (obj)
        obj.screen = self
        return self

    def render (self):
        print ('\x1b[1;1H', end = '')
        for y in range (self.height):
            for x in range (self.width):
                print (self.bg + ' ' + self.clear, end = '')
            print ()
        for obj in self.objects: obj.render ()
        print ('\x1b[{};1H'.format (self.height) )

class Object:
    def __init__ (self, graphics, foreground, background, x, y):
        self.graphics = graphics.split ('\n')
        self.fg = '\x1b[{}m'.format (30 + foreground)
        self.bg = '\x1b[{}m'.format (40 + background)
        self.clear = '\x1b[0m'
        self.x = x
        self.y = y

    def render (self):
        for y, line in enumerate (self.graphics):
            print ('\x1b[{};{}H'.format (self.y + y, self.x), end = '')
            print (self.fg + self.bg + line + self.clear)

    def collide (self, x, y):
        if y < self.y: return False
        if x < self.x: return False
        if y > self.y + len (self.graphics) - 1: return False
        if x > self.x + len (self.graphics [y - self.y] ): return False
        return True

    def move (self, dx, dy):
        nx, ny = self.x + dx, self.y + dy
        if ny < 1: return
        if ny > self.screen.height: return
        if nx < 1: return
        if nx > self.screen.width: return
        for obj in self.screen.objects:
            if obj == self: continue
            if obj.collide (nx, ny): return
        self.x, self.y = nx, ny

house = Object (house, 0, 7, 6, 3)
player = Object ('@', 1, 3, 10, 10)
s = Screen (40, 20, 3)
s += house
s += player
while True:
    c = getch ()
    if c == 'x': break
    if c == 'w': player.move (0, -1)
    if c == 's': player.move (0, 1)
    if c == 'a': player.move (-1, 0)
    if c == 'd': player.move (1, 0)
    s.render ()

这是一个截图:

enter image description here

答案 1 :(得分:1)

  

我认为这个问题是由于addstr()和addch()(我已尝试过两者)的性质,打印空白直到窗口结束,

你为什么这么认为?

首先,您实际上从未在您向我们展示的代码中调用addstr,因此不可能。

至于addch,它绝对不应该这样做 - 正如你所看到的那样,例如,从右到左画画。或者通过运行这个简单的测试代码:

# usual curses setup
stdscr.addch(10, 10, 'a')
stdscr.addch(10, 9, 'b')

如果您没有在该测试程序中看到a,那么您的终端会出现问题。但如果你是,那么你的假设是错误的,它与addch无关。


几乎可以肯定的是,你在house文件中确实有空格。在curses中,如果你在另一个角色的顶部绘制一个角色,它会替换旧角色,它不会尝试合并它们或者重击它们或类似的东西。 (这很好,因为大多数游戏机没有办法做任何这样的事情......)

如果新字符是空格,则只用空格替换旧字符。正如你所看到的那样。


所以,解决方法是删除每行末尾的所有空格,对吧?好吧,你可以做到这一点。或者你可以rstrip()每行。 (你不需要\n;你可以告诉你,你已经完成了对整条线路的迭代这一事实你已经到了最后一行,对吧?嗯,你可以在你的{ {1}}代码,或代码中没有使用file = open(…).readlines()并且只是循环文件本身;您不能使用不同的readlines()代码,但我不知道为什么你首先要做的不同。)

或者,既然你的file = open(…).read()函数非常小心地跳过了空格,也许你想在find_boundries中做同样的事情,但只是忘了?如果是这样,只需编写您想跳过空格的代码。

但是对于整个问题有一个更简单的解决方案:只需在房子之后而不是之前绘制draw,这首先不会成为问题。当然,这意味着如果玩家与房子在同一个地方,他会出现在“外面”,而不是隐藏在“内部” - 但你已经看起来有代码来防止这种情况发生,所以如果它发生了 的样子应该无关紧要。