简体   繁体   English

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

[英]Python Counter in Text-Based Game

I'm making a text-based farmville clone using objects but I need to be able to control growth rate. 我正在使用对象制作基于文本的Farmville克隆,但我需要能够控制增长率。 I need some sort of counter that will run in the background of my program and determine how grown a crop is. 我需要某种计数器,该计数器将在程序的后台运行并确定农作物的生长方式。 for example: 例如:

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

I need something like time.sleep() but something that does not stop the program from running. 我需要类似time.sleep()的东西,但是并不能阻止程序运行。 Thanks =D 谢谢= D

If you only need to know how much the crop would have grown since you last checked, you can build this into your Crop objects: 如果您只需要知道自上次检查以来作物的生长量,则可以将其内置到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

Alternatively, you could define this functionality in a separate Growable class and have all objects that grow (eg Crop , Animal ) inherit the grow method from that superclass. 另外,您可以在单独的Growable类中定义此功能, 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)

There are different ways to do this, which depend on how you want to structure your app. 有多种方法可以执行此操作,这取决于您要如何构建应用程序。 Every game is basically running some kind of loop; 每个游戏基本上都在运行某种循环。 the question is what kind of loop you're using. 问题是您使用的是哪种循环。


For a simple "console mode" game, the loop is just a loop around input() .. While you're waiting for the user to type his input, nothing else can happen. 对于简单的“控制台模式”游戏,该循环只是围绕input()的循环。当您在等待用户键入其输入时,其他任何事情都不会发生。 And that's the problem you're trying to solve. 这就是您要解决的问题。


One way to get around this is to fake it. 解决这个问题的一种方法是伪造它。 You may not be able to run any code while you're waiting for the user's input, but you can figure out all the code you would have run, and do the same thing it would have done. 在等待用户输入时,您可能无法运行任何代码,但是您可以弄清您要运行的所有代码,并可以完成相同的操作。 If the crop is supposed to grow every 1.0 seconds up to 5 times, and it's been 3.7 seconds since the crop was planted, it's now grown 3 times. 如果该作物每1.0秒增长5倍,并且自种植以来已经是3.7秒,那么现在已经增长了3倍。 jonrsharpe's answer shows a great way to structure this. jonrsharpe的答案显示了一种很好的结构方式。


This same idea works for graphical games that are driven by a frame-rate loop, like a traditional arcade game, but even simpler. 同样的想法也适用于由帧率循环驱动的图形游戏,就像传统的街机游戏一样,但更为简单。 Each frame, you check for input, update all of your objects, do any output, then sleep until it's time for the next frame. 每帧,您都要检查输入,更新所有对象,执行任何输出,然后休眠直到下一个帧。 Because frames come at a fixed rate, you can just do things like this: 由于帧以固定速率发送,因此您可以执行以下操作:

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

A different solution is to use background threads. 另一种解决方案是使用后台线程。 While your main thread can't run any code while it's waiting around for user input, any other threads keep running. 尽管您的线程在等待用户输入时无法运行任何代码,但其他线程仍在运行。 So, you can spin off a background thread for the crop. 因此,您可以为作物裁剪背景线程 You can use your original growth method, with the time.sleep(1.0) and everything, but instead of calling self.growth(crop) , call threading.Thread(target=self.growth, args=[crop]).start() . 您可以使用原始的growth方法以及self.growth(crop) time.sleep(1.0)和其他所有方法,但是可以调用threading.Thread(target=self.growth, args=[crop]).start()而不是调用self.growth(crop) threading.Thread(target=self.growth, args=[crop]).start() That's about as simple as it gets—but that simplicity comes at a cost. 那差不多就变得简单了,但是这种简单性是有代价的。 If you have a thread for each of 80x25=2000 plots of land, you'll be using all your CPU time in the scheduler and all your memory for thread stacks. 如果每个80x25 = 2000块土地都有一个线程,则将在调度程序中使用所有CPU时间,并在线程堆栈中使用所有内存。 So, this option only works if you have only a few dozen independently-active objects. 因此,仅当您只有几十个独立活动的对象时,此选项才有效。 The other problem with threads is that you have to synchronize any objects that are used on multiple threads, or you end up with race conditions, and that can be complicated to get right. 线程的另一个问题是,您必须同步在多个线程上使用的任何对象,否则最终会出现争用条件,并且可能很难解决。


A solution to the "too many threads" problem (but not the synchronization problem) is to use a Timer . 解决“线程过多”问题(而不是同步问题)的方法是使用Timer The one built into the stdlib isn't really usable (because it creates a thread for each timer), but you can find third-party implementations that are, like timer2 . 内置在stdlib中的那个并不是真正可用的(因为它为每个计时器创建了一个线程),但是您可以找到像timer2这样的第三方实现。 So, instead of sleeping for a second and then doing the rest of your code, move the rest of your code into a function, and create a Timer that calls that function after a second: 因此,与其先休眠一秒钟然后再处理其余的代码,不如将其余的代码移到一个函数中,并创建一个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()

Now you can call self.growth(crop) normally. 现在您可以正常调用self.growth(crop) But notice how the flow of control has been turned inside-out by having to move everything after the sleep (which was in the middle of a loop) into a separate function. 但是请注意,必须将睡眠后的所有内容(在循环的中间)移动到一个单独的函数中,从而使控制流由内而外翻转。


Finally, instead of a loop around input or sleeping until the next frame, you can use a full event loop: wait until something happens, where that "something" can be user input, or a timer expiring, or anything else. 最后,您可以使用完整的事件循环,而不是围绕输入的循环或进入下一帧之前的睡眠,而要使用完整的事件循环:等到发生某种事情,其中​​“某些事情”可以是用户输入,计时器到期或其他任何事情。 This is how most GUI apps and network servers work, and it's also used in many games. 这就是大多数GUI应用程序和网络服务器的工作方式,并且在许多游戏中也使用了它。 Scheduling a timer event in an event loop program looks just like scheduling a threaded timer, but without the locks. 在事件循环程序中计划计时器事件就像在安排线程计时器一样,但是没有锁。 For example, with Tkinter, it looks like this: 例如,使用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)

One final option is to break your program up into two parts: an engine and an interface. 最后一种选择是将程序分为两部分:引擎和界面。 Put them in two separate threads (or child processes, or even entirely independent programs) that communicate over queues (or pipes or sockets), and then you can write each one the way that's most natural. 将它们放在通过队列 (或管道或套接字)进行通信的两个单独的线程(或子进程,甚至是完全独立的程序)中,然后可以用最自然的方式编写每个线程。 This also means you can replace the interface with a Tkinter GUI, a pygame full-screen graphics interface, or even a web app without rewriting any of your logic in the engine. 这也意味着您可以在不重写引擎中任何逻辑的情况下,用Tkinter GUI,pygame全屏图形界面甚至Web应用程序替换该界面。

In particular, you can write the interface as a loop around input that just checks the input queue for any changes that happened while it was waiting, and then posts any commands on the output queue for the engine. 特别是,您可以将接口编写为围绕input的循环,该接口仅检查输入队列中等待时发生的任何更改,然后将所有命令发布到引擎的输出队列中。 Then write the engine as an event loop that treats new commands on the input queue as events, or a frame-rate loop that checks the queue every frame, or whatever else makes the most sense. 然后,将引擎编写为事件循环,将输入队列中的新命令视为事件,或者将帧速率循环(每帧检查队列)或其他最有意义的帧编写为引擎。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM