1

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

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

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

我有一个事物类如下:

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() 的性质(我都尝试过)打印空白直到窗口结束,但我找不到任何关于此的文档。

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

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

4

2 回答 2

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

这是屏幕截图:

在此处输入图像描述

于 2014-01-01T01:33:39.113 回答
1

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

你为什么这么认为?

首先,你从来没有真正调用addstr你展示给我们的代码,所以不可能这样。

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

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

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


几乎可以肯定,问题是您实际上在内部文件中有空格。在 curses 中,如果你在另一个字符上绘制一个字符,它会替换旧字符,它不会尝试合并它们或覆盖它们或类似的东西。(这很好,因为大多数游戏机都没有办法做这样的事情……)

如果新字符是空格,它只是用空格替换旧字符。和你看到的一模一样。


所以,解决方法是删除每行末尾的所有空格,对吗?好吧,你可以这样做。或者你可以只是rstrip()每一行。(你不需要\n; 你可以通过你已经完成对整行的迭代来判断你已经到了一行的末尾,对吧?好吧,你可以在你的file = open(…).readlines()代码中,或者在代码中没有打扰,readlines()只是循环了文件本身;你不能在你的不同file = open(…).read()代码中,但我不知道你为什么首先在那里做不同的事情。)

或者,由于您的find_boundries函数非常小心地跳过了空格,也许您想在其中做同样的事情draw但只是忘记了?如果是这样,只需编写您打算跳过空格的代码。

但是对于整个问题有一个更简单的解决方案:只需@在房子后面而不是在它前面画,这甚至不会成为一个问题。当然这意味着如果玩家和房子在同一个地方,他会出现在房子的“外面”而不是隐藏在“里面”——但你似乎已经有了防止这种情况发生的代码,所以如果它发生了,它会是什么样子并不重要。

于 2014-01-01T01:53:09.197 回答