1"""
2Section Example 2:
3
4In this Section example we develop a very basic Pong game playable by two
5persons in the same computer (hot seat!).
6
7Each Player is abstracted as a Section that consists of a space in the screen
8where the player paddle can move.
9
10Note:
11 - Sections can live along with Views. Sections do not need to occupy 100%
12 of the screen.
13 - View methods can interact with Sections by storing a reference to each
14 one.
15 - How keyboard events can be redirected to each section depending on the
16 pressed key automatically.
17
18If Python and Arcade are installed, this example can be run from the command line with:
19python -m arcade.examples.sections_demo_2
20"""
21from __future__ import annotations
22
23import random
24
25from arcade import Window, Section, View, SpriteList, SpriteSolidColor, \
26 SpriteCircle, draw_text, draw_line
27from arcade.color import BLACK, BLUE, RED, BEAU_BLUE, GRAY
28from arcade.key import W, S, UP, DOWN
29
30PLAYER_SECTION_WIDTH = 100
31PLAYER_PADDLE_SPEED = 10
32
33
34class Player(Section):
35 """
36 A Section representing the space in the screen where the player
37 paddle can move
38 """
39
40 def __init__(self, left: int, bottom: int, width: int, height: int,
41 key_up: int, key_down: int, **kwargs):
42 super().__init__(left, bottom, width, height, accept_keyboard_keys={key_up, key_down}, **kwargs)
43
44 # keys assigned to move the paddle
45 self.key_up: int = key_up
46 self.key_down: int = key_down
47
48 # the player paddle
49 self.paddle: SpriteSolidColor = SpriteSolidColor(30, 100, color=BLACK)
50
51 # player score
52 self.score: int = 0
53
54 def setup(self):
55 # reset the player paddle position to the middle of the screen
56 self.paddle.position = self.left + 50, self.height / 2
57
58 def on_update(self, delta_time: float):
59 # update the paddle position
60 self.paddle.update()
61
62 def on_draw(self):
63 # draw sections info and score
64 if self.name == 'Left':
65 keys = 'W and S'
66 start_x = self.left + 5
67 else:
68 keys = 'UP and DOWN'
69 start_x = self.left - 290
70 draw_text(f'Player {self.name} (move paddle with: {keys})',
71 start_x, self.top - 20, BLUE, 9)
72 draw_text(f'Score: {self.score}', self.left + 20,
73 self.bottom + 20, BLUE)
74
75 # draw the paddle
76 self.paddle.draw()
77
78 def on_key_press(self, symbol: int, modifiers: int):
79 # set the paddle direction and movement speed
80 if symbol == self.key_up:
81 self.paddle.change_y = PLAYER_PADDLE_SPEED
82 else:
83 self.paddle.change_y = -PLAYER_PADDLE_SPEED
84
85 def on_key_release(self, _symbol: int, _modifiers: int):
86 # stop moving the paddle
87 self.paddle.stop()
88
89
90class Pong(View):
91
92 def __init__(self):
93 super().__init__()
94
95 # a sprite list that will hold each player paddle to
96 # check for collisions
97 self.paddles: SpriteList = SpriteList()
98
99 # we store each Section
100 self.left_player: Player = Player(
101 0, 0, PLAYER_SECTION_WIDTH, self.window.height, key_up=W,
102 key_down=S, name='Left')
103 self.right_player: Player = Player(
104 self.window.width - PLAYER_SECTION_WIDTH, 0, PLAYER_SECTION_WIDTH,
105 self.window.height, key_up=UP, key_down=DOWN, name='Right')
106
107 # add the sections to the SectionManager so Sections start to work
108 self.add_section(self.left_player)
109 self.add_section(self.right_player)
110
111 # add each paddle to the sprite list
112 self.paddles.append(self.left_player.paddle)
113 self.paddles.append(self.right_player.paddle)
114
115 # create the ball
116 self.ball: SpriteCircle = SpriteCircle(20, RED)
117
118 def setup(self):
119 # set up a new game
120
121 # set ball position in the middle
122 self.ball.position = self.window.width / 2, self.window.height / 2
123
124 # randomize ball direction and speed
125 self.ball.change_x = random.choice([-3, -2, 3, 2])
126 self.ball.change_y = random.choice([-3, -2, 3, 2])
127
128 # setup player paddles
129 self.left_player.setup()
130 self.right_player.setup()
131
132 def on_update(self, delta_time: float):
133 self.ball.update() # update the ball
134
135 # bounce the ball either at the top or at the bottom
136 if self.ball.bottom <= 0:
137 self.ball.change_y *= -1
138 elif self.ball.top >= self.window.height:
139 self.ball.change_y *= -1
140
141 # check if the ball has collided with a paddle
142 collided_paddle = self.ball.collides_with_list(self.paddles)
143 if collided_paddle:
144 # adjust ball coordinates to simplify the game
145 if collided_paddle[0] is self.left_player.paddle:
146 self.ball.left = self.left_player.paddle.right
147 else:
148 self.ball.right = self.right_player.paddle.left
149
150 # bounce the ball from the paddle
151 self.ball.change_x *= -1
152
153 # check if the ball has exited the screen in either side and
154 # end the game
155 if self.ball.right <= 0:
156 self.end_game(self.right_player)
157 elif self.ball.left >= self.window.width:
158 self.end_game(self.left_player)
159
160 def end_game(self, winner: Player):
161 """ Called when one player wins """
162 winner.score += 1 # increment the winner score
163 self.setup() # prepare a new game
164
165 def on_draw(self):
166 self.clear(BEAU_BLUE) # clear the screen
167
168 self.ball.draw() # draw the ball
169
170 half_window_x = self.window.width / 2 # middle x
171
172 # draw a line diving the screen in half
173 draw_line(half_window_x, 0, half_window_x, self.window.height, GRAY, 2)
174
175
176def main():
177 # create the window
178 window = Window(title='Two player simple Pong with Sections!')
179
180 # create the custom View
181 game = Pong()
182
183 # set up the game (start a game)
184 game.setup()
185
186 # show the view
187 window.show_view(game)
188
189 # run arcade loop
190 window.run()
191
192
193if __name__ == '__main__':
194 main()