update/fixes/spelling on pong tutorial

This commit is contained in:
tshirtman 2012-12-08 18:14:16 +01:00
parent 1ae827e881
commit 3d826160df
7 changed files with 96 additions and 123 deletions

View File

@ -33,7 +33,7 @@ concepts (:doc:`/guide/firstwidget`) and basic concepts of the kv language
(:doc:`/guide/kvlang`, :doc:`/guide/designwithkv`), you can probably skip the first 2
steps and go straight to step 3.
.. note::
.. note::
You can find the entire source code, and source code files for each step in
the Kivy examples directory under `tutorials/pong/`
@ -91,10 +91,10 @@ zeros where the player scores will be displayed.
references to the our PongGame class, these properties will automatically
update when the corresponding widget properties change. Using the Kv
language gives you automatic property binding. :)
Explaning Kv file syntax
~~~~~~~~~~~~~~~~~~~~~~~~
Explaining Kv file syntax
~~~~~~~~~~~~~~~~~~~~~~~~~
Before going on to the next step, you might want to take a closer look at
the contents of the kv file we just created and figure out what is going on.
@ -104,7 +104,7 @@ Step.
On the very first line we have::
#:kivy 1.0.9
This first line is required in every kv file. It should start with ``#:kivy``
followed by a space and the Kivy version it is intended for (So Kivy can make
sure, you have at least the required version, or handle backwards compatibility
@ -114,9 +114,9 @@ After that, we define one rule that is applied to any PongGame instance::
<PongGame>:
...
Like python, kv files use indentation to define nested blocks. A block defined
with a class name inside the ``<`` and ``>`` charachters is a
with a class name inside the ``<`` and ``>`` characters is a
:class:`~kivy.uix.widget.Widget` rule, it will be applied to any instance of
the named class. If you replaced ``PongGame`` with Widget in our example, all
Widget instances would have the vertical line and the two Label widgets inside
@ -137,7 +137,7 @@ The first block inside the ``<PongGame>`` rule we have is a canvas block:
Rectangle:
pos: self.center_x - 5, 0
size: 10, self.height
So this canvas block says that the ``PongGame`` widget itself should draw some
graphics primitives. In this case, we add a Rectangle to the canvas. We set
the pos of the rectangle to be 5 pixels left of the horizontal center of
@ -147,7 +147,7 @@ graphics like this, is that the rendered rectangle will be automatically
updated when the properties of any widgets used in the value expression change.
The last two section we add, look pretty similar. Each of them adds a Label
widget as a childwidget to the ``PongGame`` widget itself. For now the text on
widget as a child widget to the ``PongGame`` widget itself. For now the text on
both of them is just set to *"0"*, we'll have to hook that up to the actual
score once we have the logic for that implemented. But the labels already
look good, since we set a bigger font_size, and positioned them relatively
@ -159,19 +159,19 @@ case):
<PongGame>:
# ...
Label:
font_size: 70
font_size: 70
center_x: root.width / 4
top: root.top - 50
text: "0"
Label:
font_size: 70
font_size: 70
center_x: root.width * 3 / 4
top: root.top - 50
text: "0"
Add the ball
------------
@ -183,54 +183,40 @@ Ok, so we have a basic pong arena to play in, but we still need the players and
a ball to pong around. Let's start with the ball. We'll add a new `PongBall`
class to create a widget that will be our ball and make it bounce around.
.. note::
We'll just look at the python class and kv rule for PongBall first.
To make it all usable, and add the ball to the arena, you'll also need to add
the proper imports and register the `PongBall` class with the widget factory
so you can add it as a childwidget in the `<PongGame>` rule. However, don't
worry, the entire code is listed at the end of this step.
PongBall class
~~~~~~~~~~~~~~
Here is the python code for the PongBall class::
class PongBall(Widget):
# velocity of the ball on x and y axis
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
# referencelist property so we can use ball.velocity as
# referencelist property so we can use ball.velocity as
# a shorthand..just like e.g. w.pos for w.x and w.y
velocity = ReferenceListProperty(velocity_x, velocity_y)
# ``move`` function will move the ball one step. This
# will be called in equal intervals to animate the ball
def move(self):
self.pos = Vector(*self.velocity) + self.pos
self.pos = Vector(*self.velocity) + self.pos
And here is the kv rule used to draw the ball as a white circle:
.. code-block:: kv
<PongBall>:
size: 50, 50
size: 50, 50
canvas:
Ellipse:
pos: self.pos
size: self.size
size: self.size
To make it all work, you also have to add the imports for the
:doc:`/api-kivy.properties` Property classes used, the
:class:`~kivy.vector.Vector`, and the :class:`~kivy.factory.Factory` singleton.
The factory is used to register your custom classes, so that Kivy knows what
class to instantiate when you use e.g. a custom classname inside a kv rule.
Once that's done, you can add a ``PongBall`` to the ``<PongGame>`` class, just
like we added the Labels before.
:doc:`/api-kivy.properties` Property classes used and the
:class:`~kivy.vector.Vector`.
Here is the entire updated python code and kv file for this step:
@ -262,7 +248,7 @@ makes this pretty easy, by letting us schedule any function we want on the
:class:`~kivy.clock.Clock` and specify the interval::
Clock.schedule_interval(game.update, 1.0/60.0)
That line for example, would cause the ``update`` function of the game object to
be called once every 60th of a second (60 times per second).
@ -283,17 +269,17 @@ we have a reference to the game object already, we can easily schedule its new
class PongGame(Widget):
def update(self):
def update(self, dt):
# call ball.move and other stuff
pass
class PongApp(App):
def build(self):
game = PongGame()
Clock.schedule_interval(game.update, 1.0/60.0)
return game
However that still doesn't help the fact that we don't have a reference to the
``PongBall`` child widget created by the kv rule. To fix this, we can add an
@ -303,18 +289,18 @@ inside the ``update`` method and even make it bounce of the edges::
class PongGame(Widget):
ball = ObjectProperty(None)
def update(self):
def update(self, dt):
self.ball.move()
# bounce off top and bottom
if (self.ball.y < 0) or (self.ball.top > self.height):
self.ball.velocity_y *= -1
# bounce off left and right
if (self.ball.x < 0) or (self.ball.right > self.width):
self.ball.velocity_x *= -1
Don't forget to hook it up in the kv file, by giving the child widget an id
and setting the games property to that id:
@ -322,24 +308,24 @@ and setting the games property to that id:
<PongGame>:
ball: pong_ball
# ... (canvas and Labels)
PongBall:
id: pong_ball
center: self.parent.center
.. note::
At this point everything is hooked up for the ball to bounce around. If
your coding along as we go, you might be wondering why the ball isn't
.. note::
At this point everything is hooked up for the ball to bounce around. If
your coding along as we go, you might be wondering why the ball isn't
moving anywhere. The ball's velocity is set to 0 on both x and y.
In code listing below for the entire source a ``serve_ball`` method is
In code listing below for the entire source a ``serve_ball`` method is
added to the ``PongGame`` class and called in the apps ``build`` method. It sets a
random x and y velocity for the ball, and also resets the position, so we
can use it later to reset the ball when a player has scored a point.
Here is the entire code for this step:
main.py:
@ -365,7 +351,7 @@ already covered in the previous steps. Instead lets focus on how to move the
Player widgets in response to user input. You can get the whole code and kv
rules for the ``PongPaddle`` class at the end of this section.
In Kivy, a widget can react to input by implemeting the ``on_touch_down``,
In Kivy, a widget can react to input by implementing the ``on_touch_down``,
``on_touch_move`` and ``on_touch_up`` methods. By default, the Widget class
implements these methods by just calling the corresponding method on all it's
child widgets to pass on the event until one of the children returns True.
@ -374,7 +360,7 @@ Pong is pretty simple, the rackets just need to move up and down. In fact it's
so simple, we don't even really need to have the player widgets handle the
events themselves. We'll just implement the ``on_touch_move`` function for the
``PongGame`` class and have it set the position of the left or right player based
on whether the touch occured on the left or right side of the screen.
on whether the touch occurred on the left or right side of the screen.
Check the ``on_touch_move`` handler::
@ -383,12 +369,12 @@ Check the ``on_touch_move`` handler::
self.player1.center_y = touch.y
if touch.x > self.width - self.width/3:
self.player2.center_y = touch.y
We'll keep the score for each player in a
:class:`~kivy.properties.NumericProperty`. The score labels of the ``PongGame``
are kept updated by changing the static string we had in the kv file before to
the score property of our new ``PongPaddle`` child widgets. When the ball
get's out of bounce on of the sides, we'll update the score and serve the ball
gets out of bounce on of the sides, we'll update the score and serve the ball
again by changing the ``update`` method in the ``PongGame`` class. The player
class also implements a ``bounce_ball method``, so that the ball bounces
differently based on where on the racket it hits. Here is the code for the
@ -397,12 +383,12 @@ differently based on where on the racket it hits. Here is the code for the
class PongPaddle(Widget):
score = NumericProperty(0)
def bounce_ball(self, ball):
if self.collide_widget(ball):
speedup = 1.1
offset = 0.02 * Vector(0, ball.center_y-self.center_y)
ball.velocity = speedup * (offset - ball.velocity)
ball.velocity = speedup * (offset - ball.velocity)
And here it is in context. Pretty much done:
@ -425,7 +411,7 @@ Where to go now?
Have some fun
Well, the pong game is pretty much complete. If you understood all of the
things that are covered in this turoial, give yourself a pat on the back and
things that are covered in this tutorial, give yourself a pat on the back and
think about how you could improve the game. Here are a few ideas of things
you could do:
@ -443,7 +429,10 @@ you could do:
wouldn't it be cool to have a player on each side and play four people at
the same time?
.. note::
* Fix the simplistic collision check so hitting the ball with an end of
the pad makes a correct bounce.
.. note::
You can find the entire source code, and source code files for each step in
the Kivy examples directory under tutorials/pong/

View File

@ -3,9 +3,9 @@ kivy.require('1.1.1')
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty, ObjectProperty
from kivy.properties import NumericProperty, ReferenceListProperty,\
ObjectProperty
from kivy.vector import Vector
from kivy.factory import Factory
from kivy.clock import Clock
@ -15,8 +15,8 @@ class PongPaddle(Widget):
def bounce_ball(self, ball):
if self.collide_widget(ball):
vx, vy = ball.velocity
offset = (ball.center_y-self.center_y)/(self.height/2)
bounced = Vector(-1*vx, vy)
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
@ -35,11 +35,11 @@ class PongGame(Widget):
player1 = ObjectProperty(None)
player2 = ObjectProperty(None)
def serve_ball(self, vel=(4,0)):
def serve_ball(self, vel=(4, 0)):
self.ball.center = self.center
self.ball.velocity = vel
def update(self, *args):
def update(self, dt):
self.ball.move()
#bounce of paddles
@ -53,32 +53,25 @@ class PongGame(Widget):
#went of to a side to score point?
if self.ball.x < self.x:
self.player2.score += 1
self.serve_ball(vel=(4,0))
self.serve_ball(vel=(4, 0))
if self.ball.x > self.width:
self.player1.score += 1
self.serve_ball(vel=(-4,0))
self.serve_ball(vel=(-4, 0))
def on_touch_move(self, touch):
if touch.x < self.width/3:
if touch.x < self.width / 3:
self.player1.center_y = touch.y
if touch.x > self.width - self.width/3:
if touch.x > self.width - self.width / 3:
self.player2.center_y = touch.y
Factory.register("PongBall", PongBall)
Factory.register("PongPaddle", PongPaddle)
Factory.register("PongGame", PongGame)
class PongApp(App):
def build(self):
game = PongGame()
game.serve_ball()
Clock.schedule_interval(game.update, 1.0/60.0)
Clock.schedule_interval(game.update, 1.0 / 60.0)
return game
if __name__ == '__main__':
PongApp().run()

View File

@ -1,13 +1,15 @@
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
class PongGame(Widget):
pass
class PongApp(App):
def build(self):
return PongGame()
if __name__ == '__main__':
PongApp().run()

View File

@ -1,10 +1,11 @@
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
class PongGame(Widget):
pass
class PongApp(App):
def build(self):
return PongGame()

View File

@ -1,15 +1,14 @@
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty
from kivy.vector import Vector
from kivy.factory import Factory
class PongBall(Widget):
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
velocity = ReferenceListProperty(velocity_x, velocity_y)
def move(self):
self.pos = Vector(*self.velocity) + self.pos
@ -23,6 +22,5 @@ class PongApp(App):
return PongGame()
Factory.register("PongBall", PongBall)
if __name__ == '__main__':
PongApp().run()

View File

@ -1,17 +1,17 @@
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty, ObjectProperty
from kivy.properties import NumericProperty, ReferenceListProperty,\
ObjectProperty
from kivy.vector import Vector
from kivy.factory import Factory
from kivy.clock import Clock
from random import randint
class PongBall(Widget):
velocity_x = NumericProperty(1)
velocity_y = NumericProperty(0)
velocity = ReferenceListProperty(velocity_x, velocity_y)
def move(self):
self.pos = Vector(*self.velocity) + self.pos
@ -21,7 +21,7 @@ class PongGame(Widget):
def serve_ball(self):
self.ball.center = self.center
self.ball.velocity = Vector(4,0).rotate(randint(0,360))
self.ball.velocity = Vector(4, 0).rotate(randint(0, 360))
def update(self, dt):
self.ball.move()
@ -39,10 +39,9 @@ class PongApp(App):
def build(self):
game = PongGame()
game.serve_ball()
Clock.schedule_interval(game.update, 1.0/60.0)
Clock.schedule_interval(game.update, 1.0 / 60.0)
return game
Factory.register("PongBall", PongBall)
if __name__ == '__main__':
PongApp().run()

View File

@ -1,30 +1,28 @@
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty, ObjectProperty
from kivy.properties import NumericProperty, ReferenceListProperty,\
ObjectProperty
from kivy.vector import Vector
from kivy.factory import Factory
from kivy.clock import Clock
from random import randint, random
class PongPaddle(Widget):
score = NumericProperty(0)
def bounce_ball(self, ball):
if self.collide_widget(ball):
vx, vy = ball.velocity
offset = (ball.center_y-self.center_y)/(self.height/2)
bounced = Vector(-1*vx, vy)
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
ball.velocity = vel.x, vel.y + offset
class PongBall(Widget):
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
velocity = ReferenceListProperty(velocity_x, velocity_y)
def move(self):
self.pos = Vector(*self.velocity) + self.pos
@ -34,17 +32,17 @@ class PongGame(Widget):
player1 = ObjectProperty(None)
player2 = ObjectProperty(None)
def serve_ball(self, vel=(4,0)):
def serve_ball(self, vel=(4, 0)):
self.ball.center = self.center
self.ball.velocity = vel
def update(self, *args):
def update(self, dt):
self.ball.move()
#bounce of paddles
self.player1.bounce_ball(self.ball)
self.player2.bounce_ball(self.ball)
#bounce ball off bottom or top
if (self.ball.y < self.y) or (self.ball.top > self.top):
self.ball.velocity_y *= -1
@ -52,32 +50,25 @@ class PongGame(Widget):
#went of to a side to score point?
if self.ball.x < self.x:
self.player2.score += 1
self.serve_ball(vel=(4,0))
self.serve_ball(vel=(4, 0))
if self.ball.x > self.width:
self.player1.score += 1
self.serve_ball(vel=(-4,0))
self.serve_ball(vel=(-4,))
def on_touch_move(self, touch):
if touch.x < self.width/3:
if touch.x < self.width / 3:
self.player1.center_y = touch.y
if touch.x > self.width - self.width/3:
if touch.x > self.width - self.width / 3:
self.player2.center_y = touch.y
Factory.register("PongBall", PongBall)
Factory.register("PongPaddle", PongPaddle)
Factory.register("PongGame", PongGame)
class PongApp(App):
def build(self):
game = PongGame()
game.serve_ball()
Clock.schedule_interval(game.update, 1.0/60.0)
Clock.schedule_interval(game.update, 1.0 / 60.0)
return game
if __name__ == '__main__':
PongApp().run()