Kivy - 试图了解Clock.schedule_interval

时间:2014-11-19 22:04:29

标签: python kivy

我是Kivy的新手,我正在做pong教程,一切都很顺利,我把它改编成4个玩家,每个球拍都有自己的颜色,如果球击中桨,它会得到它的颜色,这样如果球离开屏幕,那么最后一名击球手就会得分。

我在Ubuntu 14,04中使用virtualenv。

为了便于下载和检查,可以在此处找到所有代码:Pong_kivy

有什么问题?

比赛结束后,重新启动球似乎每次都在增加速度,直到它崩溃,这不是由速度矢量引起的,我检查过它,因此我认为我处理错误的Clock.Schedule功能。我似乎无法找到什么错误,所以任何帮助都表示赞赏。

pong.py文件

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty,\
    ObjectProperty
from kivy.vector import Vector
from kivy.clock import Clock
from kivy.graphics import *
from kivy.uix.button import Button, Label
from random import randint


class PongPaddle(Widget):
    score = NumericProperty(0)
    orientation = ObjectProperty([0, 0])
    color = ObjectProperty([0,0,0,1])

def bounce_ball(self, ball):
    if self.collide_widget(ball):
        vx, vy = ball.velocity
        ball.color = self.color
        ball.last_hit = int(self.custom_id)
        if self.orientation[0] == 25:                
            offset = (ball.center_y - self.center_y) / (self.height / 2)
            bounced = Vector(-1 * vx, vy)
            vel = bounced * 1.1
            ball.velocity = vel.x, vel.y + offset
        else:
            offset = (ball.center_x - self.center_x) / (self.width / 2)
            bounced = Vector(vx, -1 * vy)
            vel = bounced * 1.1
            ball.velocity = vel.x + offset, vel.y

class PongBall(Widget):
    velocity_x = NumericProperty(0)
    velocity_y = NumericProperty(0)
    last_hit = 0
    color = ObjectProperty([0,0,0,1])
    velocity = ReferenceListProperty(velocity_x, velocity_y)

    def move(self):
        self.pos = Vector(*self.velocity) + self.pos


class PongGame(Widget):
    ball = ObjectProperty(None)
    player1 = ObjectProperty(None)
    player2 = ObjectProperty(None)
    player3 = ObjectProperty(None)
    player4 = ObjectProperty(None)
    l = Label()
    btn1 = Button(text='Restart')
    win_game = 1

    def start(self):
        Clock.schedule_interval(self.update, 1.0 / 60.0)

    def stop(self):
        # Stop updating
        Clock.unschedule(self.update)

    def init(self):
        ## Setup players
        self.player1.orientation = [25, 200]
        self.player1.color = [1,0,0,1]
        self.player1.score = 0
        # Player 2
        self.player2.orientation = [25, 200]
        self.player2.color = [0,1,0,1]
        self.player2.score = 0
        # Player 3
        self.player3.orientation = [200, 25]
        self.player3.color = [0,0,1,1]
        self.player3.score = 0
        # Player 4
        self.player4.orientation = [200, 25]
        self.player4.color = [1,1,0,1]
        self.player4.score = 0

    def serve_ball(self):
        # Ball velocity - Add 2 to avoid 0
        vel = (randint(-8,6)+2, randint(-8,6)+2)

        # Setup ball
        self.ball.center = self.center
        self.ball.velocity = vel
        self.ball.last_hit = 0
        self.ball.color = [1,1,1,1]

    def update(self, dt):
        self.ball.move()

        #Bounce out the of paddles - Why do all check? Only can bounce on one any given time
        self.player1.bounce_ball(self.ball)
        self.player2.bounce_ball(self.ball)
        self.player3.bounce_ball(self.ball)
        self.player4.bounce_ball(self.ball)

        #bounce ball off bottom or top - This is for 2 players game
        # if (self.ball.y < self.y) or (self.ball.top > self.top):
        #     self.ball.velocity_y *= -1

        # Went of any side? - Last hitter gets a goal
        if self.ball.x < self.x or self.ball.x > self.width or self.ball.y < self.y or self.ball.y > self.height:
            if self.ball.last_hit == 1:
                self.player1.score += 1
            elif self.ball.last_hit == 2:
                self.player2.score += 1
            elif self.ball.last_hit == 3:
                self.player3.score += 1
            elif self.ball.last_hit == 4:
                self.player4.score += 1
            self.serve_ball()

        if self.player1.score >= self.win_game:
            self.player_win(1)
        elif self.player2.score >= self.win_game:
            self.player_win(2)
        elif self.player3.score >= self.win_game:
            self.player_win(3)
        elif self.player4.score >= self.win_game:
            self.player_win(4)

    def player_win(self, player_int):
        # Remove Ball and players
        self.clear_widgets()
        # Configure Label and Btn
        self.l.text ='Player ' + str(player_int) + ' Wins!!'
        self.l.top = self.top-50
        self.l.font_size = 50
        self.l.center_x = self.width/2
        self.btn1.bind(on_press=self.restart)
        self.btn1.center_x = self.width/2
        self.btn1.top = self.top/2
        self.add_widget(self.l)
        self.add_widget(self.btn1)
        self.stop()


    def on_touch_move(self, touch):
        if touch.x < self.width / 3 and touch.y > self.height / 6 \
            and touch.y < 5 * self.height/6:
            self.player1.center_y = touch.y
        if touch.x > self.width - self.width / 3 and touch.y > self.height / 6 \
            and touch.y < 5 * self.height / 6:
            self.player2.center_y = touch.y
        if touch.y < self.height / 3 and touch.x > self.width / 6 \
            and touch.x < 5 * self.width / 6:
            self.player4.center_x = touch.x
        if touch.y > 2* self.height / 3 and touch.x > self.width / 6 \
            and touch.x < 5 * self.width / 6:
            self.player3.center_x = touch.x

    # Method update  layout
    def update_rect(instance, value):
        instance.rect.pos = instance.pos
        instance.rect.size = instance.size

    def restart(self, instance):
        # Remove btn and labels
        self.clear_widgets()

        # Add what I want
        self.add_widget(self.ball)
        self.add_widget(self.player1)
        self.add_widget(self.player2)
        self.add_widget(self.player3)
        self.add_widget(self.player4)
        self.init()
        self.serve_ball()
        self.start()

class PongApp(App):
    def build(self):
        game = PongGame()
        game.init()
        game.serve_ball()
        game.start()
        return game


if __name__ == '__main__':
    PongApp().run()

pong.kv文件

#:kivy 1.0.9

<PongBall>:
    size: 50, 50 
    canvas:
        Color:
            rgba: self.color[0], self.color[1], self.color[2], self.color[3]
        Ellipse:
            pos: self.pos
            size: self.size          

<PongPaddle>:
    size: root.orientation[0], root.orientation[1]
    canvas:
        Color:
            rgba: self.color[0], self.color[1], self.color[2], self.color[3]
        Rectangle:
            pos:self.pos
            size:self.size

<PongGame>:
    ball: pong_ball
    player1: player_left
    player2: player_right
    player3: player_top
    player4: player_bottom

    canvas.before:
        Rectangle:
            pos: self.pos
            size: self.size
            source: 'black.jpg'

    Label:
        font_size: 15  
        center_x: root.width / 8
        top: self.height*0.95
        text: "P1: " + str(root.player1.score) 
    Label:
        font_size: 15  
        center_x: root.width / 8
        top: self.height*0.80
        text: "P2: " + str(root.player2.score)
    Label:
        font_size: 15  
        center_x: root.width / 5
        top: self.height*0.95
        text: "P3: " + str(root.player3.score) 
    Label:
        font_size: 15  
        center_x: root.width / 5
        top: self.height*0.80
        text: "P4: " + str(root.player4.score)


    PongBall:
        id: pong_ball
        center: self.parent.center

    PongPaddle:
        id: player_left
        custom_id: "1"
        x: root.x
        center_y: root.center_y

    PongPaddle:
        id: player_right
        custom_id: "2"
        x: root.width-self.width
        center_y: root.center_y

    PongPaddle:
        id: player_top
        custom_id: "3"
        y: root.height - self.height
        center_x: root.center_x

    PongPaddle:
        id: player_bottom
        custom_id: "4"
        y: root.y
        center_x: root.center_x

3 个答案:

答案 0 :(得分:1)

问题似乎不是时钟。 restart方法被多次调用,似乎是因为每次调用on_press时都会绑定init()来电 如果将该行移动到__init__函数,则只调用一次绑定,按钮仅在每次按下时调用一次重启,并且不会发生错误:

def __init__(self, **kwargs):
    super(PongGame, self).__init__(**kwargs)
    self.btn1.bind(on_press=self.restart)

答案 1 :(得分:0)

通过更改:

我发现了什么问题
 def start(self):
        Clock.schedule_interval(self.update, 1.0 / 60.0)

要:

def start(self):
    Clock.unschedule(self.update)
    Clock.schedule_interval(self.update, 1.0 / 60.0)

我不明白为什么需要这种改变,但似乎我在不同的线程中创建事件,也许有人可以更好地解释它。

答案 2 :(得分:0)

请参阅Kivy文档中的9.2.3触发器事件

有时您可能希望将某个函数安排为下一帧仅被调用一次,从而防止 重复通话。您可能会很想这样实现:

示例代码:

2.27

这种对触发器进行编程的方法非常昂贵,因为即使事件已经完成,您也总是会调用未安排的时间。此外,每次都会创建一个新事件。改用触发器:


provider "aws" {
  version                 = "2.27.0"
}

每次调用trigger()时,都会安排一次回调调用。如果已经安排了,则不会重新安排。