基于文本的Gam中的Python计数器

2024-06-01 14:04:05 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在使用对象制作一个基于文本的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>

我需要一些东西时间。睡觉()但它不会停止程序的运行。 谢谢=D


Tags: 对象作物文本self程序object计数器情况
2条回答

如果只需要知道自上次检查后作物的生长量,可以将其构建到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方法。在

^{pr2}$

有不同的方法来实现这一点,这取决于你想要如何构建你的应用程序。每个游戏基本上都在运行某种循环;问题是你使用的是哪种循环。在


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


解决这个问题的一个方法就是假装。在等待用户输入时,您可能无法运行任何代码,但您可以计算出您将要运行的所有代码,并执行与该代码相同的操作。如果作物应该每1.0秒生长一次,最多5次,而自从作物种植以来已经有3.7秒了,那么现在它已经长了3次了。jornsharpe的回答显示了一个很好的方法来构建这个结构。在


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

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时间和线程堆栈的所有内存。因此,只有在只有几十个独立活动对象时,此选项才有效。线程的另一个问题是,必须同步多个线程上使用的任何对象,否则最终会出现竞态条件,这可能会很复杂。在


“线程过多”问题(但不是同步问题)的解决方案是使用^{}。stdlib中内置的那个并不实用(因为它为每个计时器创建一个线程),但是您可以找到第三方实现,比如^{}。因此,与其先睡一秒钟然后再执行其余代码,不如将其余代码移到函数中,然后创建一个计时器,在一秒钟后调用该函数:

^{pr2}$

现在您可以正常调用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的循环,它只检查输入队列等待时发生的任何更改,然后在引擎的输出队列上发布任何命令。然后将引擎写成偶数将输入队列上的新命令视为事件的t循环,或每帧检查队列的帧速率循环,或其他最有意义的循环。在

相关问题 更多 >