基于文本的游戏中的Python计数器

时间:2013-11-20 05:32:18

标签: python python-3.x

我正在使用对象制作基于文本的farmville克隆,但我需要能够控制增长率。我需要某种计数器,它将在我的程序的后台运行并确定作物的生长方式。 例如:

class Grow(object):
    def growth(self, crop):
        self.grown = 0
        while self.grown < 5:
            <every x number of seconds add one to self.grown>

我需要像time.sleep()这样的东西,但不能阻止程序运行。 谢谢= D

2 个答案:

答案 0 :(得分:1)

如果您只需要知道自上次检查后作物的生长量,您可以将其构建到Crop个对象中:

from datetime import datetime

class Crop:

    RATE = 1 # rate of growth, units per second

    def __init__(self, ..., grown=0): # allow starting growth to be set
        ...
        self.last_update = datetime.now()
        self.grown = grown

    def grow(self):
        """Set current growth based on time since last update."""
        now = datetime.now()
        self.grown += Crop.RATE * (now - self.last_update).seconds
        self.last_update = now

或者,您可以在单独的Growable类中定义此功能,并使所有增长的对象(例如CropAnimal)从该超类继承grow方法

class Growable:

    def __init__(self, grown=0):
        self.last_update = datetime.now()
        self.grown = grown

    def grow(self, rate):
        """Set current growth based on time since last update and rate."""
        now = datetime.now()
        self.grown += rate * (now - self.last_update).seconds
        self.last_update = now

class Crop(Growable):

    RATE = 1

    def __init__(self, ..., grown=0):
        super().__init__(grown)
        ...

    def grow(self):
        super().grow(Crop.RATE)

答案 1 :(得分:0)

有不同的方法可以执行此操作,具体取决于您希望如何构建应用。每场比赛基本上都在运行某种循环;问题是你正在使用什么样的循环。


对于一个简单的“控制台模式”游戏,循环只是围绕input()的循环。当你在等待用户输入他的输入时,没有其他任何事情可以发生。这就是你要解决的问题。


解决这个问题的一种方法是伪造它。在等待用户输入时,您可能无法运行任何代码,但您可以找出 运行的所有代码,并执行与此相同的操作。如果作物应该每1.0秒生长5次,并且自种植作物以来已经是3.7秒,它现在增长了3倍。 jonrsharpe的答案显示了一种很好的结构方法。


同样的想法适用于由帧率循环驱动的图形游戏,就像传统的街机游戏一样,但更简单。每一帧,你检查输入,更新所有对象,做任何输出,然后睡觉,直到下一帧的时间。因为帧是固定的,你可以这样做:

def grow(self, rate):
    self.grown += rate / FRAMES_PER_SECOND

另一种解决方案是使用后台线程。虽然 main 线程在等待用户输入时无法运行任何代码,但任何其他线程仍在运行。因此,您可以为作物分离background thread。您可以使用原始的growth方法,time.sleep(1.0)和所有内容,但不要拨打self.growth(crop),而是拨打threading.Thread(target=self.growth, args=[crop]).start()。这就像它变得一样简单 - 但这种简单性是有代价的。如果你有80x25 = 2000的每个地块的线程,你将使用调度程序中的所有CPU时间和线程堆栈的所有内存。因此,此选项仅在您只有几十个独立活动对象时才有效。线程的另一个问题是你必须同步在多个线程上使用的任何对象,否则你最终会遇到竞争条件,而这可能很复杂。


“太多线程”问题(但不是同步问题)的解决方案是使用Timer。 stdlib中内置的那个并不真正可用(因为它为每个计时器创建一个线程),但你可以找到像timer2这样的第三方实现。因此,不是暂时休眠然后再执行其余代码,而是将其余代码移动到函数中,并创建一个在一秒钟后调用该函数的Timer:

def growth(self, crop):
    self.grown = 0
    def grow_callback():
        with self.lock:
            if self.grown < 5:
                self.grown += 1
                Timer(1.0, grow_callback).start()
    Timer(1.0, grow_callback).start()

现在您可以正常致电self.growth(crop)。但是请注意,在睡眠之后(在循环中间)将所有内容移动到一个单独的函数中,控制流如何由内而外转变。


最后,您可以使用完整的事件循环来代替输入或睡眠循环直到下一帧:等到某事发生,其中“某事”可以是用户输入,或者计时器到期,或其他任何东西。这就是大多数GUI应用程序和网络服务器的工作方式,并且它也用于许多游戏中。在事件循环程序中调度计时器事件看起来就像调度线程计时器,但没有锁定。例如,使用Tkinter,它看起来像这样:

def growth(self, crop):
    self.grown = 0
    def grow_callback():
        if self.grown < 5:
            self.grown += 1
            self.after(1000, function=grow_callback)
    self.after(1000, function=grow_callback)

最后一个选择是将程序分为两部分:引擎和界面。将它们放在两个独立的线程(或子进程,甚至完全独立的程序)中,通过queues(或管道或套接字)进行通信,然后您可以用最自然的方式编写每个线程。这也意味着您可以使用Tkinter GUI,pygame全屏图形界面甚至Web应用程序替换界面,而无需重写引擎中的任何逻辑。

特别是,您可以将接口编写为input周围的循环,它只检查输入队列中等待时发生的任何更改,然后在引擎的输出队列上发布任何命令。然后将引擎编写为事件循环,将输入队列上的新命令视为事件,或者每帧检查队列的帧速率循环,或其他任何最有意义的队列。