The Fastest Text Drawing: pyglet Batches

This example demonstrates the most efficient way to render
arcade.Text
objects: adding them to pyglet’s
Batch
. Otherwise, it is the
same as the Better Text Drawing with Text Objects example.
For a much simpler and slower approach, see Slow but Easy Text Drawing.
drawing_text_objects.py
1"""
2The current fastest way to draw text with arcade.
3
4This example improves on the other two text-drawing examples
5by using pyglet's batch functionality.
6
7Although pyglet's batches do not support non-drawing features like
8Arcade's SpriteList, they offer similar benefits for drawing. Adding
9arcade.Text objects to a batch allows drawing thousands of them with
10almost the same cost as drawing a single one directly.
11
12If Python and Arcade are installed, this example can be run from the command line with:
13python -m arcade.examples.drawing_text_objects_batch
14"""
15import arcade
16from pyglet.graphics import Batch
17
18# Load fonts bundled with Arcade such as the Kenney fonts
19arcade.resources.load_kenney_fonts()
20arcade.resources.load_liberation_fonts()
21
22WINDOW_WIDTH = 1280 # Window width in pixels
23WINDOW_HEIGHT = 800 # Window height in pixels
24WINDOW_TITLE = "Drawing Text Example" # Window title
25DEFAULT_LINE_HEIGHT = 45 # Line height to use in pixels
26DEFAULT_FONT_SIZE = 20 # Default font size in points
27
28
29class GameView(arcade.View):
30 """
31 Main application class.
32 """
33
34 def __init__(self):
35 super().__init__()
36
37 self.background_color = arcade.color.BEIGE
38 self.text_angle = 0
39 self.time_elapsed = 0.0
40
41 self.batch = Batch()
42
43 # Add the screen title
44 start_x = 0
45 start_y = WINDOW_HEIGHT - DEFAULT_LINE_HEIGHT * 1.5
46 self.title = arcade.Text(
47 "Text Drawing Examples",
48 start_x,
49 start_y,
50 arcade.color.BLACK,
51 DEFAULT_FONT_SIZE * 2,
52 width=WINDOW_WIDTH,
53 align="center",
54 batch=self.batch,
55 )
56
57 # start_x and start_y make the start point for the text. We draw a dot to make it
58 # easy too see the text in relation to its start x and y.
59 start_x = 10
60 start_y = WINDOW_HEIGHT - DEFAULT_LINE_HEIGHT * 3
61 self.fonts = arcade.Text(
62 "Fonts:",
63 start_x,
64 start_y,
65 arcade.color.FRENCH_WINE,
66 DEFAULT_FONT_SIZE, bold=True,
67 batch=self.batch,
68 )
69
70 # Move the y value down to create another line of text
71 start_y -= DEFAULT_LINE_HEIGHT
72 self.font_default = arcade.Text(
73 "Default Font (Arial)",
74 start_x,
75 start_y,
76 arcade.color.BLACK,
77 DEFAULT_FONT_SIZE,
78 batch=self.batch,
79 )
80
81 # Show some built-in fonts
82 start_y -= DEFAULT_LINE_HEIGHT
83 self.font_kenney_blocks = arcade.Text(
84 "Kenney Blocks Font",
85 start_x,
86 start_y,
87 arcade.color.BLACK,
88 DEFAULT_FONT_SIZE,
89 font_name="Kenney Blocks",
90 batch=self.batch,
91 )
92
93 start_y -= DEFAULT_LINE_HEIGHT
94 self.font_kenney_future = arcade.Text(
95 "Kenney Future Font",
96 start_x,
97 start_y,
98 arcade.color.BLACK,
99 DEFAULT_FONT_SIZE,
100 font_name="Kenney Future",
101 batch=self.batch,
102 )
103
104 start_y -= DEFAULT_LINE_HEIGHT
105 self.font_kenney_high = arcade.Text(
106 "Kenney High Font",
107 start_x,
108 start_y,
109 arcade.color.BLACK,
110 DEFAULT_FONT_SIZE,
111 font_name="Kenney High",
112 batch=self.batch,
113 )
114
115 start_y -= DEFAULT_LINE_HEIGHT
116 self.font_kenney_high_square = arcade.Text(
117 "Kenney High Square Font",
118 start_x,
119 start_y,
120 arcade.color.BLACK,
121 DEFAULT_FONT_SIZE,
122 font_name="Kenney High Square",
123 batch=self.batch,
124 )
125
126 start_y -= DEFAULT_LINE_HEIGHT
127 self.font_kenney_mini_square = arcade.Text(
128 "Kenney Mini Square Font",
129 start_x, start_y,
130 arcade.color.BLACK,
131 DEFAULT_FONT_SIZE,
132 font_name="Kenney Mini Square",
133 batch=self.batch,
134 )
135
136 start_y -= DEFAULT_LINE_HEIGHT
137 self.font_kenney_pixel = arcade.Text(
138 "Kenney Pixel Font",
139 start_x, start_y,
140 arcade.color.BLACK,
141 DEFAULT_FONT_SIZE,
142 font_name="Kenney Pixel",
143 batch=self.batch,
144 )
145
146 start_y -= DEFAULT_LINE_HEIGHT
147 self.font_kenney_pixel_square = arcade.Text(
148 "Kenney Pixel Square Font",
149 start_x, start_y,
150 arcade.color.BLACK,
151 DEFAULT_FONT_SIZE,
152 font_name="Kenney Pixel Square",
153 batch=self.batch,
154 )
155
156 start_y -= DEFAULT_LINE_HEIGHT
157 self.font_kenney_rocket = arcade.Text(
158 "Kenney Rocket Font",
159 start_x, start_y,
160 arcade.color.BLACK,
161 DEFAULT_FONT_SIZE,
162 font_name="Kenney Rocket",
163 batch=self.batch,
164 )
165
166 start_y -= DEFAULT_LINE_HEIGHT
167 self.font_kenney_rocket_square = arcade.Text(
168 "Kenney Rocket Square Font",
169 start_x, start_y,
170 arcade.color.BLACK,
171 DEFAULT_FONT_SIZE,
172 font_name="Kenney Rocket Square",
173 batch=self.batch,
174 )
175
176 start_y -= DEFAULT_LINE_HEIGHT
177 # When trying to use system fonts, it can be risky to specify
178 # only a single font because someone else's computer might not
179 # have it installed. This is especially true if they run a
180 # different operating system. For example, if you are on Windows
181 # and a friend has a mac or Linux, they might not have the same
182 # fonts. Your game could look different or broken on their computer.
183 # One way around that is to provide multiple options for draw_text
184 # to try. It will use the first one it finds, and use Arial as a
185 # default if it can't find any of them.
186 # In the example below, draw_text is given a tuple of names for very
187 # similar fonts, each of which is common on a different major
188 # operating systems.
189 self.font_times_new_roman = arcade.Text(
190 "Times New Roman (Or closest match on system)",
191 start_x, start_y,
192 arcade.color.BLACK,
193 DEFAULT_FONT_SIZE,
194 font_name=(
195 "Times New Roman", # Comes with Windows
196 "Times", # MacOS may sometimes have this variant
197 # Common on Linux systems + we ship it with Arcade
198 "Liberation Serif"
199 ),
200 batch=self.batch,
201 )
202
203 start_y -= DEFAULT_LINE_HEIGHT
204 self.multi_line_breaks = arcade.Text(
205 "Multi-Line\ntext using\n\\n characters.",
206 start_x, start_y,
207 arcade.color.BLACK,
208 DEFAULT_FONT_SIZE / 2,
209 multiline=True,
210 width=300,
211 batch=self.batch,
212 )
213
214 start_y -= DEFAULT_LINE_HEIGHT * 1.5
215 self.multiline_wrap = arcade.Text(
216 "Wrapping really long text automatically to a new line. "
217 "The quick brown fox jumped over the lazy dogs.",
218 start_x,
219 start_y,
220 arcade.color.BLACK,
221 DEFAULT_FONT_SIZE / 2,
222 multiline=True,
223 width=300,
224 batch=self.batch,
225 )
226
227 # --- Column 2 ---
228 start_x = 750
229 start_y = WINDOW_HEIGHT - DEFAULT_LINE_HEIGHT * 3
230 self.text_positioning = arcade.Text(
231 "Text Positioning:",
232 start_x,
233 start_y,
234 arcade.color.FRENCH_WINE,
235 DEFAULT_FONT_SIZE,
236 bold=True,
237 batch=self.batch,
238 )
239
240 # start_x and start_y make the start point for the text.
241 # We draw a dot to make it easy too see the text in relation to
242 # its start x and y.
243 start_y -= DEFAULT_LINE_HEIGHT
244
245 self.default_baseline_left = arcade.Text(
246 "Default of 'baseline' and 'Left'",
247 start_x,
248 start_y,
249 arcade.color.BLACK,
250 DEFAULT_FONT_SIZE,
251 batch=self.batch,
252 )
253
254 start_y -= DEFAULT_LINE_HEIGHT
255 self.bottom_left = arcade.Text(
256 "'bottom' and 'left'",
257 start_x,
258 start_y,
259 arcade.color.BLACK,
260 DEFAULT_FONT_SIZE,
261 anchor_x="left",
262 anchor_y="bottom",
263 batch=self.batch,
264 )
265
266 start_y -= DEFAULT_LINE_HEIGHT
267 self.top_left = arcade.Text(
268 "'top' and 'left'",
269 start_x, start_y,
270 arcade.color.BLACK,
271 DEFAULT_FONT_SIZE,
272 anchor_x="left",
273 anchor_y="top",
274 batch=self.batch,
275 )
276
277 start_y -= DEFAULT_LINE_HEIGHT * 2
278 self.basline_center = arcade.Text(
279 "'baseline' and 'center'",
280 start_x, start_y,
281 arcade.color.BLACK,
282 DEFAULT_FONT_SIZE,
283 anchor_x="center",
284 anchor_y="baseline",
285 batch=self.batch,
286 )
287
288 start_y -= DEFAULT_LINE_HEIGHT
289 self.baseline_right = arcade.Text(
290 "'baseline' and 'right'",
291 start_x,
292 start_y,
293 arcade.color.BLACK,
294 DEFAULT_FONT_SIZE,
295 anchor_x="right",
296 anchor_y="baseline",
297 batch=self.batch,
298 )
299
300 start_y -= DEFAULT_LINE_HEIGHT
301 self.center_center = arcade.Text(
302 "'center' and 'center'",
303 start_x,
304 start_y,
305 arcade.color.BLACK,
306 DEFAULT_FONT_SIZE,
307 anchor_x="center",
308 anchor_y="center",
309 batch=self.batch,
310 )
311
312 start_y -= DEFAULT_LINE_HEIGHT * 4
313 # start_x = 0
314 # start_y = 0
315 self.rotating_text = arcade.Text(
316 "Rotating Text",
317 start_x, start_y,
318 arcade.color.BLACK,
319 DEFAULT_FONT_SIZE,
320 anchor_x="center",
321 anchor_y="center",
322 rotation=0,
323 batch=self.batch,
324 )
325
326 def on_update(self, delta_time):
327 self.text_angle += 1
328 self.time_elapsed += delta_time
329 self.rotating_text.rotation = self.text_angle
330
331 def on_draw(self):
332 """
333 Render the screen.
334 """
335 # This command should happen before we start drawing. It will clear
336 # the screen to the background color, and erase what we drew last frame.
337 self.clear()
338
339 # Draw all the text objects
340 self.batch.draw()
341
342 arcade.draw_point(
343 self.default_baseline_left.x,
344 self.default_baseline_left.y,
345 arcade.color.BARN_RED,
346 5,
347 )
348
349 arcade.draw_point(
350 self.bottom_left.x,
351 self.bottom_left.y,
352 arcade.color.BARN_RED,
353 5,
354 )
355
356 arcade.draw_point(
357 self.top_left.x,
358 self.top_left.y,
359 arcade.color.BARN_RED,
360 5,
361 )
362
363 arcade.draw_point(
364 self.basline_center.x,
365 self.basline_center.y,
366 arcade.color.BARN_RED,
367 5
368 )
369
370 arcade.draw_point(
371 self.baseline_right.x,
372 self.baseline_right.y,
373 arcade.color.BARN_RED,
374 5,
375 )
376
377 arcade.draw_point(
378 self.center_center.x,
379 self.center_center.y,
380 arcade.color.BARN_RED,
381 5,
382 )
383
384 arcade.draw_point(
385 self.rotating_text.x,
386 self.rotating_text.y,
387 arcade.color.BARN_RED,
388 5,
389 )
390
391 def on_key_press(self, symbol: int, modifiers: int):
392 """ Handle key press events """
393 if symbol == arcade.key.ESCAPE:
394 self.window.close()
395
396
397def main():
398 """ Main function """
399 # Create a window class. This is what actually shows up on screen
400 window = arcade.Window(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE)
401
402 # Create and setup the GameView
403 game = GameView()
404
405 # Show GameView on screen
406 window.show_view(game)
407
408 # Start the arcade game loop
409 arcade.run()
410
411
412if __name__ == "__main__":
413 main()