optimize speed by rendering text by parts

This commit is contained in:
adrienmalin 2019-09-27 22:14:44 +02:00
parent a4d61337b7
commit 51edef2474
825 changed files with 205 additions and 31697 deletions

View File

@ -1,46 +0,0 @@
"""
The Arcade Library
A Python simple, easy to use module for creating 2D games.
"""
# Error out if we import Arcade with an incompatible version of Python.
import sys
if sys.version_info[0] < 3 or (sys.version_info[0] == 3 and sys.version_info[1] < 6):
sys.exit("The Arcade Library requires Python 3.6 or higher.")
try:
import pyglet_ffmpeg2
except Exception as e:
print("Unable to load the ffmpeg library. ", e)
import pyglet
pyglet.options['shadow_window'] = False
from arcade import color
from arcade import csscolor
from arcade import key
from arcade.application import *
from arcade.arcade_types import *
from arcade.utils import *
from arcade.draw_commands import *
from arcade.buffered_draw_commands import *
from arcade.geometry import *
from arcade.physics_engines import *
from arcade.emitter import *
from arcade.emitter_simple import *
from arcade.particle import *
from arcade.sound import *
from arcade.sprite import *
from arcade.sprite_list import *
from arcade.version import *
from arcade.window_commands import *
from arcade.joysticks import *
from arcade.read_tiled_map import *
from arcade.isometric import *
from arcade.text import draw_text
from arcade.text import create_text
from arcade.text import render_text
from arcade import tilemap

View File

@ -1,446 +0,0 @@
"""
The main window class that all object-oriented applications should
derive from.
"""
from numbers import Number
from typing import Tuple
import pyglet.gl as gl
import pyglet
from arcade.window_commands import (get_viewport, set_viewport, set_window)
MOUSE_BUTTON_LEFT = 1
MOUSE_BUTTON_MIDDLE = 2
MOUSE_BUTTON_RIGHT = 4
class NoOpenGLException(Exception):
"""
Exception when we can't get an OpenGL 3.3+ context
"""
pass
class Window(pyglet.window.Window):
"""
The Window class forms the basis of most advanced games that use Arcade.
It represents a window on the screen, and manages events.
"""
def __init__(self, width: Number = 800, height: Number = 600,
title: str = 'Arcade Window', fullscreen: bool = False,
resizable: bool = False, update_rate=1/60,
antialiasing: bool = True):
"""
Construct a new window
:param float width: Window width
:param float height: Window height
:param str title: Title (appears in title bar)
:param bool fullscreen: Should this be full screen?
:param bool resizable: Can the user resize the window?
:param float update_rate: How frequently to update the window.
:param bool antialiasing: Should OpenGL's anti-aliasing be enabled?
"""
if antialiasing:
config = pyglet.gl.Config(major_version=3,
minor_version=3,
double_buffer=True,
sample_buffers=1,
samples=4)
else:
config = pyglet.gl.Config(major_version=3,
minor_version=3,
double_buffer=True)
try:
super().__init__(width=width, height=height, caption=title,
resizable=resizable, config=config)
except pyglet.window.NoSuchConfigException:
raise NoOpenGLException("Unable to create an OpenGL 3.3+ context. "
"Check to make sure your system supports OpenGL 3.3 or higher.")
if antialiasing:
try:
gl.glEnable(gl.GL_MULTISAMPLE_ARB)
except pyglet.gl.GLException:
print("Warning: Anti-aliasing not supported on this computer.")
if update_rate:
from pyglet import compat_platform
if compat_platform == 'darwin' or compat_platform == 'linux':
# Set vsync to false, or we'll be limited to a 1/30 sec update rate possibly
self.context.set_vsync(False)
self.set_update_rate(update_rate)
super().set_fullscreen(fullscreen)
self.invalid = False
set_window(self)
set_viewport(0, self.width, 0, self.height)
self.current_view = None
def update(self, delta_time: float):
"""
Move everything. For better consistency in naming, use ``on_update`` instead.
:param float delta_time: Time interval since the last time the function was called in seconds.
"""
try:
self.current_view.update(delta_time)
except AttributeError:
pass
def on_update(self, delta_time: float):
"""
Move everything. Perform collision checks. Do all the game logic here.
:param float delta_time: Time interval since the last time the function was called.
"""
try:
self.current_view.on_update(delta_time)
except AttributeError:
pass
def set_update_rate(self, rate: float):
"""
Set how often the screen should be updated.
For example, self.set_update_rate(1 / 60) will set the update rate to 60 fps
:param float rate: Update frequency in seconds
"""
pyglet.clock.unschedule(self.update)
pyglet.clock.schedule_interval(self.update, rate)
pyglet.clock.unschedule(self.on_update)
pyglet.clock.schedule_interval(self.on_update, rate)
def on_mouse_motion(self, x: float, y: float, dx: float, dy: float):
"""
Override this function to add mouse functionality.
:param float x: x position of mouse
:param float y: y position of mouse
:param float dx: Change in x since the last time this method was called
:param float dy: Change in y since the last time this method was called
"""
pass
def on_mouse_press(self, x: float, y: float, button: int, modifiers: int):
"""
Override this function to add mouse button functionality.
:param float x: x position of the mouse
:param float y: y position of the mouse
:param int button: What button was hit. One of:
arcade.MOUSE_BUTTON_LEFT, arcade.MOUSE_BUTTON_RIGHT,
arcade.MOUSE_BUTTON_MIDDLE
:param int modifiers: Shift/click, ctrl/click, etc.
"""
pass
def on_mouse_drag(self, x: float, y: float, dx: float, dy: float, buttons: int, modifiers: int):
"""
Override this function to add mouse button functionality.
:param float x: x position of mouse
:param float y: y position of mouse
:param float dx: Change in x since the last time this method was called
:param float dy: Change in y since the last time this method was called
:param int buttons: Which button is pressed
:param int modifiers: Ctrl, shift, etc.
"""
self.on_mouse_motion(x, y, dx, dy)
def on_mouse_release(self, x: float, y: float, button: int,
modifiers: int):
"""
Override this function to add mouse button functionality.
:param float x:
:param float y:
:param int button:
:param int modifiers:
"""
pass
def on_mouse_scroll(self, x: int, y: int, scroll_x: int, scroll_y: int):
"""
User moves the scroll wheel.
:param int x:
:param int y:
:param int scroll_x:
:param int scroll_y:
"""
pass
def set_mouse_visible(self, visible: bool = True):
"""
If true, user can see the mouse cursor while it is over the window. Set false,
the mouse is not visible. Default is true.
:param bool visible:
"""
super().set_mouse_visible(visible)
def on_key_press(self, symbol: int, modifiers: int):
"""
Override this function to add key press functionality.
:param int symbol: Key that was hit
:param int modifiers: If it was shift/ctrl/alt
"""
pass
def on_key_release(self, symbol: int, modifiers: int):
"""
Override this function to add key release functionality.
:param int symbol: Key that was hit
:param int modifiers: If it was shift/ctrl/alt
"""
pass
def on_draw(self):
"""
Override this function to add your custom drawing code.
"""
pass
def on_resize(self, width: float, height: float):
"""
Override this function to add custom code to be called any time the window
is resized.
:param float width: New width
:param float height: New height
"""
original_viewport = self.get_viewport()
# unscaled_viewport = self.get_viewport_size()
# scaling = unscaled_viewport[0] / width
self.set_viewport(original_viewport[0],
original_viewport[0] + width,
original_viewport[2],
original_viewport[2] + height)
def set_min_size(self, width: float, height: float):
""" Wrap the Pyglet window call to set minimum size
:param float width: width in pixels.
:param float height: height in pixels.
"""
if self._resizable:
super().set_minimum_size(width, height)
else:
raise ValueError('Cannot set min size on non-resizable window')
def set_max_size(self, width: float, height: float):
""" Wrap the Pyglet window call to set maximum size
:param float width: width in pixels.
:param float height: height in pixels.
:Raises ValueError:
"""
if self._resizable:
super().set_maximum_size(width, height)
else:
raise ValueError('Cannot set max size on non-resizable window')
def set_size(self, width: float, height: float):
"""
Ignore the resizable flag and set the size
:param float width:
:param float height:
"""
super().set_size(width, height)
def get_size(self) -> Tuple[int, int]:
"""
Get the size of the window.
:returns: (width, height)
"""
return super().get_size()
def get_location(self) -> Tuple[int, int]:
"""
Return the X/Y coordinates of the window
:returns: x, y of window location
"""
return super().get_location()
def set_visible(self, visible=True):
"""
Set if the window is visible or not. Normally, a program's window is visible.
:param bool visible:
"""
super().set_visible(visible)
def set_viewport(self, left: Number, right: Number, bottom: Number, top: Number):
"""
Set the viewport. (What coordinates we can see.
Used to scale and/or scroll the screen.)
:param Number left:
:param Number right:
:param Number bottom:
:param Number top:
"""
set_viewport(left, right, bottom, top)
def get_viewport(self) -> (float, float, float, float):
""" Get the viewport. (What coordinates we can see.) """
return get_viewport()
def test(self, frames: int = 10):
"""
Used by unit test cases. Runs the event loop a few times and stops.
:param int frames:
"""
for i in range(frames):
self.switch_to()
self.dispatch_events()
self.dispatch_event('on_draw')
self.flip()
self.update(1/60)
def show_view(self, new_view: 'View'):
if not isinstance(new_view, View):
raise ValueError("Must pass an arcade.View object to "
"Window.show_view()")
# Store the Window that is showing the "new_view" View.
if new_view.window is None:
new_view.window = self
elif new_view.window != self:
raise RuntimeError("You are attempting to pass the same view "
"object between multiple windows. A single "
"view object can only be used in one window.")
# remove previously shown view's handlers
if self.current_view is not None:
self.remove_handlers(self.current_view)
# push new view's handlers
self.current_view = new_view
self.push_handlers(self.current_view)
self.current_view.on_show()
# Note: After the View has been pushed onto pyglet's stack of event handlers (via push_handlers()), pyglet
# will still call the Window's event handlers. (See pyglet's EventDispatcher.dispatch_event() implementation
# for details)
def _create(self):
super()._create()
def _recreate(self, changes):
super()._recreate(changes)
def flip(self):
super().flip()
def switch_to(self):
super().switch_to()
def set_caption(self, caption):
super().set_caption(caption)
def set_minimum_size(self, width, height):
super().set_minimum_size(width, height)
def set_maximum_size(self, width, height):
super().set_maxiumum_size(width, height)
def set_location(self, x, y):
super().set_location(x, y)
def activate(self):
super().activate()
def minimize(self):
super().minimize()
def maximize(self):
super().maximize()
def set_vsync(self, vsync):
super().set_vsync(vsync)
def set_mouse_platform_visible(self, platform_visible=None):
super().set_mouse_platform_visible(platform_visible)
def set_exclusive_mouse(self, exclusive=True):
super().set_exclusive_mouse(exclusive)
def set_exclusive_keyboard(self, exclusive=True):
super().set_exclusive_keyboard(exclusive)
def get_system_mouse_cursor(self, name):
super().get_system_mouse_cursor(name)
def dispatch_events(self):
super().dispatch_events()
def open_window(width: Number, height: Number, window_title: str, resizable: bool = False,
antialiasing: bool = True) -> Window:
"""
This function opens a window. For ease-of-use we assume there will only be one window, and the
programmer does not need to keep a handle to the window. This isn't the best architecture, because
the window handle is stored in a global, but it makes things easier for programmers if they don't
have to track a window pointer.
:param Number width: Width of the window.
:param Number height: Height of the window.
:param str window_title: Title of the window.
:param bool resizable: Whether the window can be user-resizable.
:param bool antialiasing: Smooth the graphics?
:returns: Handle to window
:rtype arcade.Window:
"""
global _window
_window = Window(width, height, window_title, resizable, update_rate=None,
antialiasing=antialiasing)
return _window
class View:
"""
TODO:Thoughts:
- is there a need for a close()/on_close() method?
"""
def __init__(self):
self.window = None
def update(self, delta_time: float):
"""To be overridden"""
pass
def on_update(self, delta_time: float):
"""To be overridden"""
pass
def on_draw(self):
"""Called when this view should draw"""
pass
def on_show(self):
"""Called when this view is shown"""
pass

View File

@ -1,13 +0,0 @@
"""
Module specifying data custom types used for type hinting.
"""
from typing import Tuple
from typing import List
from typing import Union
RGB = Union[Tuple[int, int, int], List[int]]
RGBA = Union[Tuple[int, int, int, int], List[int]]
Color = Union[RGB, RGBA]
Point = Union[Tuple[float, float], List[float]]
Vector = Point
PointList = Union[Tuple[Point, ...], List[Point]]

View File

@ -1,793 +0,0 @@
"""
Drawing commands that use vertex buffer objects (VBOs).
This module contains commands for basic graphics drawing commands,
but uses Vertex Buffer Objects. This keeps the vertices loaded on
the graphics card for much faster render times.
"""
import math
import itertools
from collections import defaultdict
import pyglet.gl as gl
import numpy as np
from typing import Iterable
from typing import List
from typing import TypeVar
from typing import Generic
from arcade.arcade_types import Color
from arcade.draw_commands import rotate_point
from arcade.arcade_types import PointList
from arcade.draw_commands import get_four_byte_color
from arcade.draw_commands import get_projection
from arcade.draw_commands import _get_points_for_thick_line
from arcade import shader
class VertexBuffer:
"""
This class represents a `vertex buffer object`_ for internal library use. Clients
of the library probably don't need to use this.
Attributes:
:vbo_id: ID of the vertex buffer as assigned by OpenGL
:size:
:width:
:height:
:color:
.. _vertex buffer object:
https://en.wikipedia.org/wiki/Vertex_Buffer_Object
"""
def __init__(self, vbo_vertex_id: gl.GLuint, size: float, draw_mode: int, vbo_color_id: gl.GLuint = None):
self.vbo_vertex_id = vbo_vertex_id
self.vbo_color_id = vbo_color_id
self.size = size
self.draw_mode = draw_mode
self.color = None
self.line_width = 0
class Shape:
def __init__(self):
self.vao = None
self.vbo = None
self.program = None
self.mode = None
self.line_width = 1
def draw(self):
# program['Projection'].write(get_projection().tobytes())
with self.vao:
assert(self.line_width == 1)
gl.glLineWidth(self.line_width)
gl.glEnable(gl.GL_BLEND)
gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
gl.glEnable(gl.GL_LINE_SMOOTH)
gl.glHint(gl.GL_LINE_SMOOTH_HINT, gl.GL_NICEST)
gl.glHint(gl.GL_POLYGON_SMOOTH_HINT, gl.GL_NICEST)
gl.glEnable(gl.GL_PRIMITIVE_RESTART)
gl.glPrimitiveRestartIndex(2 ** 32 - 1)
self.vao.render(mode=self.mode)
def create_line(start_x: float, start_y: float, end_x: float, end_y: float,
color: Color, line_width: float = 1) -> Shape:
"""
Create a line to be rendered later. This works faster than draw_line because
the vertexes are only loaded to the graphics card once, rather than each frame.
:param float start_x:
:param float start_y:
:param float end_x:
:param float end_y:
:param Color color:
:param float line_width:
:Returns Shape:
"""
points = _get_points_for_thick_line(start_x, start_y, end_x, end_y, line_width)
color_list = [color, color, color, color]
triangle_point_list = points[1], points[0], points[2], points[3]
shape = create_triangles_filled_with_colors(triangle_point_list, color_list)
return shape
def create_line_generic_with_colors(point_list: PointList,
color_list: Iterable[Color],
shape_mode: int,
line_width: float = 1) -> Shape:
"""
This function is used by ``create_line_strip`` and ``create_line_loop``,
just changing the OpenGL type for the line drawing.
:param PointList point_list:
:param Iterable[Color] color_list:
:param float shape_mode:
:param float line_width:
:Returns Shape:
"""
program = shader.program(
vertex_shader='''
#version 330
uniform mat4 Projection;
in vec2 in_vert;
in vec4 in_color;
out vec4 v_color;
void main() {
gl_Position = Projection * vec4(in_vert, 0.0, 1.0);
v_color = in_color;
}
''',
fragment_shader='''
#version 330
in vec4 v_color;
out vec4 f_color;
void main() {
f_color = v_color;
}
''',
)
buffer_type = np.dtype([('vertex', '2f4'), ('color', '4B')])
data = np.zeros(len(point_list), dtype=buffer_type)
data['vertex'] = point_list
data['color'] = [get_four_byte_color(color) for color in color_list]
vbo = shader.buffer(data.tobytes())
vao_content = [
shader.BufferDescription(
vbo,
'2f 4B',
('in_vert', 'in_color'),
normalized=['in_color']
)
]
vao = shader.vertex_array(program, vao_content)
with vao:
program['Projection'] = get_projection().flatten()
shape = Shape()
shape.vao = vao
shape.vbo = vbo
shape.program = program
shape.mode = shape_mode
shape.line_width = line_width
return shape
def create_line_generic(point_list: PointList,
color: Color,
shape_mode: int, line_width: float = 1) -> Shape:
"""
This function is used by ``create_line_strip`` and ``create_line_loop``,
just changing the OpenGL type for the line drawing.
"""
colors = [get_four_byte_color(color)] * len(point_list)
shape = create_line_generic_with_colors(
point_list,
colors,
shape_mode,
line_width)
return shape
def create_line_strip(point_list: PointList,
color: Color, line_width: float = 1):
"""
Create a multi-point line to be rendered later. This works faster than draw_line because
the vertexes are only loaded to the graphics card once, rather than each frame.
:param PointList point_list:
:param Color color:
:param PointList line_width:
:Returns Shape:
"""
if line_width == 1:
return create_line_generic(point_list, color, gl.GL_LINE_STRIP, line_width)
else:
triangle_point_list = []
new_color_list = []
for i in range(1, len(point_list)):
start_x = point_list[i - 1][0]
start_y = point_list[i - 1][1]
end_x = point_list[i][0]
end_y = point_list[i][1]
color1 = color
color2 = color
points = _get_points_for_thick_line(start_x, start_y, end_x, end_y, line_width)
new_color_list += color1, color2, color1, color2
triangle_point_list += points[1], points[0], points[2], points[3]
shape = create_triangles_filled_with_colors(triangle_point_list, new_color_list)
return shape
def create_line_loop(point_list: PointList,
color: Color, line_width: float = 1):
"""
Create a multi-point line loop to be rendered later. This works faster than draw_line because
the vertexes are only loaded to the graphics card once, rather than each frame.
:param PointList point_list:
:param Color color:
:param float line_width:
:Returns Shape:
"""
point_list = list(point_list) + [point_list[0]]
return create_line_generic(point_list, color, gl.GL_LINE_STRIP, line_width)
def create_lines(point_list: PointList,
color: Color, line_width: float = 1):
"""
Create a multi-point line loop to be rendered later. This works faster than draw_line because
the vertexes are only loaded to the graphics card once, rather than each frame.
:param PointList point_list:
:param Color color:
:param float line_width:
:Returns Shape:
"""
return create_line_generic(point_list, color, gl.GL_LINES, line_width)
def create_lines_with_colors(point_list: PointList,
color_list: List[Color],
line_width: float = 1):
if line_width == 1:
return create_line_generic_with_colors(point_list, color_list, gl.GL_LINES, line_width)
else:
triangle_point_list = []
new_color_list = []
for i in range(1, len(point_list), 2):
start_x = point_list[i-1][0]
start_y = point_list[i-1][1]
end_x = point_list[i][0]
end_y = point_list[i][1]
color1 = color_list[i-1]
color2 = color_list[i]
points = _get_points_for_thick_line(start_x, start_y, end_x, end_y, line_width)
new_color_list += color1, color1, color2, color2
triangle_point_list += points[1], points[0], points[2], points[3]
shape = create_triangles_filled_with_colors(triangle_point_list, new_color_list)
return shape
def create_polygon(point_list: PointList,
color: Color):
"""
Draw a convex polygon. This will NOT draw a concave polygon.
Because of this, you might not want to use this function.
:param PointList point_list:
:param color:
:Returns Shape:
"""
# We assume points were given in order, either clockwise or counter clockwise.
# Polygon is assumed to be monotone.
# To fill the polygon, we start by one vertex, and we chain triangle strips
# alternating with vertices to the left and vertices to the right of the
# initial vertex.
half = len(point_list) // 2
interleaved = itertools.chain.from_iterable(
itertools.zip_longest(point_list[:half], reversed(point_list[half:]))
)
point_list = [p for p in interleaved if p is not None]
return create_line_generic(point_list, color, gl.GL_TRIANGLE_STRIP, 1)
def create_rectangle_filled(center_x: float, center_y: float, width: float,
height: float, color: Color,
tilt_angle: float = 0) -> Shape:
"""
Create a filled rectangle.
:param float center_x:
:param float center_y:
:param float width:
:param float height:
:param Color color:
:param float tilt_angle:
:Returns Shape:
"""
return create_rectangle(center_x, center_y, width, height,
color, tilt_angle=tilt_angle)
def create_rectangle_outline(center_x: float, center_y: float, width: float,
height: float, color: Color,
border_width: float = 1, tilt_angle: float = 0) -> Shape:
"""
Create a rectangle outline.
Args:
center_x:
center_y:
width:
height:
color:
border_width:
tilt_angle:
Returns:
"""
return create_rectangle(center_x, center_y, width, height,
color, border_width, tilt_angle, filled=False)
def get_rectangle_points(center_x: float, center_y: float, width: float,
height: float, tilt_angle: float = 0) -> PointList:
"""
Utility function that will return all four coordinate points of a
rectangle given the x, y center, width, height, and rotation.
Args:
center_x:
center_y:
width:
height:
tilt_angle:
Returns:
"""
x1 = -width / 2 + center_x
y1 = -height / 2 + center_y
x2 = -width / 2 + center_x
y2 = height / 2 + center_y
x3 = width / 2 + center_x
y3 = height / 2 + center_y
x4 = width / 2 + center_x
y4 = -height / 2 + center_y
if tilt_angle:
x1, y1 = rotate_point(x1, y1, center_x, center_y, tilt_angle)
x2, y2 = rotate_point(x2, y2, center_x, center_y, tilt_angle)
x3, y3 = rotate_point(x3, y3, center_x, center_y, tilt_angle)
x4, y4 = rotate_point(x4, y4, center_x, center_y, tilt_angle)
data = [(x1, y1),
(x2, y2),
(x3, y3),
(x4, y4)]
return data
def create_rectangle(center_x: float, center_y: float, width: float,
height: float, color: Color,
border_width: float = 1, tilt_angle: float = 0,
filled=True) -> Shape:
"""
This function creates a rectangle using a vertex buffer object.
Creating the rectangle, and then later drawing it with ``render_rectangle``
is faster than calling ``draw_rectangle``.
Args:
center_x:
center_y:
width:
height:
color:
border_width:
tilt_angle:
filled:
Returns:
"""
data = get_rectangle_points(center_x, center_y, width, height, tilt_angle)
if filled:
shape_mode = gl.GL_TRIANGLE_STRIP
data[-2:] = reversed(data[-2:])
else:
i_lb = center_x - width / 2 + border_width / 2, center_y - height / 2 + border_width / 2
i_rb = center_x + width / 2 - border_width / 2, center_y - height / 2 + border_width / 2
i_rt = center_x + width / 2 - border_width / 2, center_y + height / 2 - border_width / 2
i_lt = center_x - width / 2 + border_width / 2, center_y + height / 2 - border_width / 2
o_lb = center_x - width / 2 - border_width / 2, center_y - height / 2 - border_width / 2
o_rb = center_x + width / 2 + border_width / 2, center_y - height / 2 - border_width / 2
o_rt = center_x + width / 2 + border_width / 2, center_y + height / 2 + border_width / 2
o_lt = center_x - width / 2 - border_width / 2, center_y + height / 2 + border_width / 2
data = o_lt, i_lt, o_rt, i_rt, o_rb, i_rb, o_lb, i_lb, o_lt, i_lt
if tilt_angle != 0:
point_list_2 = []
for point in data:
new_point = rotate_point(point[0], point[1], center_x, center_y, tilt_angle)
point_list_2.append(new_point)
data = point_list_2
border_width = 1
shape_mode = gl.GL_TRIANGLE_STRIP
# _generic_draw_line_strip(point_list, color, gl.GL_TRIANGLE_STRIP)
# shape_mode = gl.GL_LINE_STRIP
# data.append(data[0])
shape = create_line_generic(data, color, shape_mode, border_width)
return shape
def create_rectangle_filled_with_colors(point_list, color_list) -> Shape:
"""
This function creates one rectangle/quad using a vertex buffer object.
Creating the rectangles, and then later drawing it with ``render``
is faster than calling ``draw_rectangle``.
"""
shape_mode = gl.GL_TRIANGLE_STRIP
new_point_list = [point_list[0], point_list[1], point_list[3], point_list[2]]
new_color_list = [color_list[0], color_list[1], color_list[3], color_list[2]]
return create_line_generic_with_colors(new_point_list, new_color_list, shape_mode)
def create_rectangles_filled_with_colors(point_list, color_list) -> Shape:
"""
This function creates multiple rectangle/quads using a vertex buffer object.
Creating the rectangles, and then later drawing it with ``render``
is faster than calling ``draw_rectangle``.
"""
shape_mode = gl.GL_TRIANGLES
new_point_list = []
new_color_list = []
for i in range(0, len(point_list), 4):
new_point_list += [point_list[0 + i], point_list[1 + i], point_list[3 + i]]
new_point_list += [point_list[1 + i], point_list[3 + i], point_list[2 + i]]
new_color_list += [color_list[0 + i], color_list[1 + i], color_list[3 + i]]
new_color_list += [color_list[1 + i], color_list[3 + i], color_list[2 + i]]
return create_line_generic_with_colors(new_point_list, new_color_list, shape_mode)
def create_triangles_filled_with_colors(point_list, color_list) -> Shape:
"""
This function creates multiple rectangle/quads using a vertex buffer object.
Creating the rectangles, and then later drawing it with ``render``
is faster than calling ``draw_rectangle``.
"""
shape_mode = gl.GL_TRIANGLE_STRIP
return create_line_generic_with_colors(point_list, color_list, shape_mode)
def create_ellipse_filled(center_x: float, center_y: float,
width: float, height: float, color: Color,
tilt_angle: float = 0, num_segments: int = 128) -> Shape:
"""
Create a filled ellipse. Or circle if you use the same width and height.
"""
border_width = 1
return create_ellipse(center_x, center_y, width, height, color,
border_width, tilt_angle, num_segments, filled=True)
def create_ellipse_outline(center_x: float, center_y: float,
width: float, height: float, color: Color,
border_width: float = 1,
tilt_angle: float = 0, num_segments: int = 128) -> Shape:
"""
Create an outline of an ellipse.
"""
return create_ellipse(center_x, center_y, width, height, color,
border_width, tilt_angle, num_segments, filled=False)
def create_ellipse(center_x: float, center_y: float,
width: float, height: float, color: Color,
border_width: float = 1,
tilt_angle: float = 0, num_segments: int = 32,
filled=True) -> Shape:
"""
This creates an ellipse vertex buffer object (VBO). It can later be
drawn with ``render_ellipse_filled``. This method of drawing an ellipse
is much faster than calling ``draw_ellipse_filled`` each frame.
Note: This can't be unit tested on Appveyor because its support for OpenGL is
poor.
"""
# Create an array with the vertex point_list
point_list = []
for segment in range(num_segments):
theta = 2.0 * 3.1415926 * segment / num_segments
x = width * math.cos(theta) + center_x
y = height * math.sin(theta) + center_y
if tilt_angle:
x, y = rotate_point(x, y, center_x, center_y, tilt_angle)
point_list.append((x, y))
if filled:
half = len(point_list) // 2
interleaved = itertools.chain.from_iterable(
itertools.zip_longest(point_list[:half], reversed(point_list[half:]))
)
point_list = [p for p in interleaved if p is not None]
shape_mode = gl.GL_TRIANGLE_STRIP
else:
point_list.append(point_list[0])
shape_mode = gl.GL_LINE_STRIP
return create_line_generic(point_list, color, shape_mode, border_width)
def create_ellipse_filled_with_colors(center_x: float, center_y: float,
width: float, height: float,
outside_color: Color, inside_color: Color,
tilt_angle: float = 0, num_segments: int = 32) -> Shape:
"""
Draw an ellipse, and specify inside/outside color. Used for doing gradients.
:param float center_x:
:param float center_y:
:param float width:
:param float height:
:param Color outside_color:
:param float inside_color:
:param float tilt_angle:
:param int num_segments:
:Returns Shape:
"""
# Create an array with the vertex data
# Create an array with the vertex point_list
point_list = [(center_x, center_y)]
for segment in range(num_segments):
theta = 2.0 * 3.1415926 * segment / num_segments
x = width * math.cos(theta) + center_x
y = height * math.sin(theta) + center_y
if tilt_angle:
x, y = rotate_point(x, y, center_x, center_y, tilt_angle)
point_list.append((x, y))
point_list.append(point_list[1])
color_list = [inside_color] + [outside_color] * (num_segments + 1)
return create_line_generic_with_colors(point_list, color_list, gl.GL_TRIANGLE_FAN)
T = TypeVar('T', bound=Shape)
class ShapeElementList(Generic[T]):
"""
A program can put multiple drawing primitives in a ShapeElementList, and then
move and draw them as one. Do this when you want to create a more complex object
out of simpler primitives. This also speeds rendering as all objects are drawn
in one operation.
"""
def __init__(self):
"""
Initialize the sprite list
"""
# List of sprites in the sprite list
self.shape_list = []
self.change_x = 0
self.change_y = 0
self._center_x = 0
self._center_y = 0
self._angle = 0
self.program = shader.program(
vertex_shader='''
#version 330
uniform mat4 Projection;
uniform vec2 Position;
uniform float Angle;
in vec2 in_vert;
in vec4 in_color;
out vec4 v_color;
void main() {
float angle = radians(Angle);
mat2 rotate = mat2(
cos(angle), sin(angle),
-sin(angle), cos(angle)
);
gl_Position = Projection * vec4(Position + (rotate * in_vert), 0.0, 1.0);
v_color = in_color;
}
''',
fragment_shader='''
#version 330
in vec4 v_color;
out vec4 f_color;
void main() {
f_color = v_color;
}
''',
)
# Could do much better using just one vbo and glDrawElementsBaseVertex
self.batches = defaultdict(_Batch)
self.dirties = set()
def append(self, item: T):
"""
Add a new shape to the list.
"""
self.shape_list.append(item)
group = (item.mode, item.line_width)
self.batches[group].items.append(item)
self.dirties.add(group)
def remove(self, item: T):
"""
Remove a specific shape from the list.
"""
self.shape_list.remove(item)
group = (item.mode, item.line_width)
self.batches[group].items.remove(item)
self.dirties.add(group)
def _refresh_shape(self, group):
# Create a buffer large enough to hold all the shapes buffers
batch = self.batches[group]
total_vbo_bytes = sum(s.vbo.size for s in batch.items)
vbo = shader.Buffer.create_with_size(total_vbo_bytes)
offset = 0
gl.glBindBuffer(gl.GL_COPY_WRITE_BUFFER, vbo.buffer_id)
# Copy all the shapes buffer in our own vbo
for shape in batch.items:
gl.glBindBuffer(gl.GL_COPY_READ_BUFFER, shape.vbo.buffer_id)
gl.glCopyBufferSubData(
gl.GL_COPY_READ_BUFFER,
gl.GL_COPY_WRITE_BUFFER,
gl.GLintptr(0),
gl.GLintptr(offset),
shape.vbo.size)
offset += shape.vbo.size
# Create an index buffer object. It should count starting from 0. We need to
# use a reset_idx to indicate that a new shape will start.
reset_idx = 2 ** 32 - 1
indices = []
counter = itertools.count()
for shape in batch.items:
indices.extend(itertools.islice(counter, shape.vao.num_vertices))
indices.append(reset_idx)
del indices[-1]
indices = np.array(indices)
ibo = shader.Buffer(indices.astype('i4').tobytes())
vao_content = [
shader.BufferDescription(
vbo,
'2f 4B',
('in_vert', 'in_color'),
normalized=['in_color']
)
]
vao = shader.vertex_array(self.program, vao_content, ibo)
with self.program:
self.program['Projection'] = get_projection().flatten()
self.program['Position'] = [self.center_x, self.center_y]
self.program['Angle'] = self.angle
batch.shape.vao = vao
batch.shape.vbo = vbo
batch.shape.ibo = ibo
batch.shape.program = self.program
mode, line_width = group
batch.shape.mode = mode
batch.shape.line_width = line_width
def move(self, change_x: float, change_y: float):
"""
Move all the shapes ion the list
:param change_x: Amount to move on the x axis
:param change_y: Amount to move on the y axis
"""
self.center_x += change_x
self.center_y += change_y
def __len__(self) -> int:
""" Return the length of the sprite list. """
return len(self.shape_list)
def __iter__(self) -> Iterable[T]:
""" Return an iterable object of sprites. """
return iter(self.shape_list)
def __getitem__(self, i):
return self.shape_list[i]
def draw(self):
"""
Draw everything in the list.
"""
for group in self.dirties:
self._refresh_shape(group)
self.dirties.clear()
for batch in self.batches.values():
batch.shape.draw()
def _get_center_x(self) -> float:
"""Get the center x coordinate of the ShapeElementList."""
return self._center_x
def _set_center_x(self, value: float):
"""Set the center x coordinate of the ShapeElementList."""
self._center_x = value
with self.program:
self.program['Position'] = [self._center_x, self._center_y]
center_x = property(_get_center_x, _set_center_x)
def _get_center_y(self) -> float:
"""Get the center y coordinate of the ShapeElementList."""
return self._center_y
def _set_center_y(self, value: float):
"""Set the center y coordinate of the ShapeElementList."""
self._center_y = value
with self.program:
self.program['Position'] = [self._center_x, self._center_y]
center_y = property(_get_center_y, _set_center_y)
def _get_angle(self) -> float:
"""Get the angle of the ShapeElementList in degrees."""
return self._angle
def _set_angle(self, value: float):
"""Set the angle of the ShapeElementList in degrees."""
self._angle = value
with self.program:
self.program['Angle'] = self._angle
angle = property(_get_angle, _set_angle)
class _Batch(Generic[T]):
def __init__(self):
self.shape = Shape()
self.items = []

File diff suppressed because it is too large Load Diff

View File

@ -1,152 +0,0 @@
"""
This module pre-defines colors as defined by the W3C CSS standard:
https://www.w3.org/TR/2018/PR-css-color-3-20180315/
"""
ALICE_BLUE = (240, 248, 255)
ANTIQUE_WHITE = (250, 235, 215)
AQUA = (0, 255, 255)
AQUAMARINE = (127, 255, 212)
AZURE = (240, 255, 255)
BEIGE = (245, 245, 220)
BISQUE = (255, 228, 196)
BLACK = (0, 0, 0)
BLANCHED_ALMOND = (255, 235, 205)
BLUE = (0, 0, 255)
BLUE_VIOLET = (138, 43, 226)
BROWN = (165, 42, 42)
BURLYWOOD = (222, 184, 135)
CADET_BLUE = (95, 158, 160)
CHARTREUSE = (127, 255, 0)
CHOCOLATE = (210, 105, 30)
CORAL = (255, 127, 80)
CORNFLOWER_BLUE = (100, 149, 237)
CORNSILK = (255, 248, 220)
CRIMSON = (220, 20, 60)
CYAN = (0, 255, 255)
DARK_BLUE = (0, 0, 139)
DARK_CYAN = (0, 139, 139)
DARK_GOLDENROD = (184, 134, 11)
DARK_GRAY = (169, 169, 169)
DARK_GREEN = (0, 100, 0)
DARK_GREY = (169, 169, 169)
DARK_KHAKI = (189, 183, 107)
DARK_MAGENTA = (139, 0, 139)
DARK_OLIVE_GREEN = (85, 107, 47)
DARK_ORANGE = (255, 140, 0)
DARK_ORCHID = (153, 50, 204)
DARK_RED = (139, 0, 0)
DARK_SALMON = (233, 150, 122)
DARK_SEA_GREEN = (143, 188, 143)
DARK_SLATE_BLUE = (72, 61, 139)
DARK_SLATE_GRAY = (47, 79, 79)
DARK_SLATE_GREY = (47, 79, 79)
DARK_TURQUOISE = (0, 206, 209)
DARK_VIOLET = (148, 0, 211)
DEEP_PINK = (255, 20, 147)
DEEP_SKY_BLUE = (0, 191, 255)
DIM_GRAY = (105, 105, 105)
DIM_GREY = (105, 105, 105)
DODGER_BLUE = (30, 144, 255)
FIREBRICK = (178, 34, 34)
FLORAL_WHITE = (255, 250, 240)
FOREST_GREEN = (34, 139, 34)
FUCHSIA = (255, 0, 255)
GAINSBORO = (220, 220, 220)
GHOST_WHITE = (248, 248, 255)
GOLD = (255, 215, 0)
GOLDENROD = (218, 165, 32)
GRAY = (128, 128, 128)
GREEN = (0, 128, 0)
GREENYELLOW = (173, 255, 47)
GREY = (128, 128, 128)
HONEYDEW = (240, 255, 240)
HOTPINK = (255, 105, 180)
INDIANRED = (205, 92, 92)
INDIGO = (75, 0, 130)
IVORY = (255, 255, 240)
KHAKI = (240, 230, 140)
LAVENDER = (230, 230, 250)
LAVENDER_BLUSH = (255, 240, 245)
LAWNGREEN = (124, 252, 0)
LEMON_CHIFFON = (255, 250, 205)
LIGHT_BLUE = (173, 216, 230)
LIGHT_CORAL = (240, 128, 128)
LIGHT_CYAN = (224, 255, 255)
LIGHT_GOLDENROD_YELLOW = (250, 250, 210)
LIGHT_GRAY = (211, 211, 211)
LIGHT_GREEN = (144, 238, 144)
LIGHT_GREY = (211, 211, 211)
LIGHT_PINK = (255, 182, 193)
LIGHT_SALMON = (255, 160, 122)
LIGHT_SEA_GREEN = (32, 178, 170)
LIGHT_SKY_BLUE = (135, 206, 250)
LIGHT_SLATE_GRAY = (119, 136, 153)
LIGHT_SLATE_GREY = (119, 136, 153)
LIGHT_STEEL_BLUE = (176, 196, 222)
LIGHT_YELLOW = (255, 255, 224)
LIME = (0, 255, 0)
LIME_GREEN = (50, 205, 50)
LINEN = (250, 240, 230)
MAGENTA = (255, 0, 255)
MAROON = (128, 0, 0)
MEDIUM_AQUAMARINE = (102, 205, 170)
MEDIUM_BLUE = (0, 0, 205)
MEDIUM_ORCHID = (186, 85, 211)
MEDIUM_PURPLE = (147, 112, 219)
MEDIUM_SEA_GREEN = (60, 179, 113)
MEDIUM_SLATE_BLUE = (123, 104, 238)
MEDIUM_SPRING_GREEN = (0, 250, 154)
MEDIUM_TURQUOISE = (72, 209, 204)
MEDIUM_VIOLET_RED = (199, 21, 133)
MIDNIGHT_BLUE = (25, 25, 112)
MINT_CREAM = (245, 255, 250)
MISTY_ROSE = (255, 228, 225)
MOCCASIN = (255, 228, 181)
NAVAJO_WHITE = (255, 222, 173)
NAVY = (0, 0, 128)
OLD_LACE = (253, 245, 230)
OLIVE = (128, 128, 0)
OLIVE_DRAB = (107, 142, 35)
ORANGE = (255, 165, 0)
ORANGE_RED = (255, 69, 0)
ORCHID = (218, 112, 214)
PALE_GOLDENROD = (238, 232, 170)
PALE_GREEN = (152, 251, 152)
PALE_TURQUOISE = (175, 238, 238)
PALE_VIOLET_RED = (219, 112, 147)
PAPAYA_WHIP = (255, 239, 213)
PEACH_PUFF = (255, 218, 185)
PERU = (205, 133, 63)
PINK = (255, 192, 203)
PLUM = (221, 160, 221)
POWDER_BLUE = (176, 224, 230)
PURPLE = (128, 0, 128)
RED = (255, 0, 0)
ROSY_BROWN = (188, 143, 143)
ROYAL_BLUE = (65, 105, 225)
SADDLE_BROWN = (139, 69, 19)
SALMON = (250, 128, 114)
SANDY_BROWN = (244, 164, 96)
SEA_GREEN = (46, 139, 87)
SEASHELL = (255, 245, 238)
SIENNA = (160, 82, 45)
SILVER = (192, 192, 192)
SKY_BLUE = (135, 206, 235)
SLATE_BLUE = (106, 90, 205)
SLATE_GRAY = (112, 128, 144)
SLATE_GREY = (112, 128, 144)
SNOW = (255, 250, 250)
SPRING_GREEN = (0, 255, 127)
STEEL_BLUE = (70, 130, 180)
TAN = (210, 180, 140)
TEAL = (0, 128, 128)
THISTLE = (216, 191, 216)
TOMATO = (255, 99, 71)
TURQUOISE = (64, 224, 208)
VIOLET = (238, 130, 238)
WHEAT = (245, 222, 179)
WHITE = (255, 255, 255)
WHITE_SMOKE = (245, 245, 245)
YELLOW = (255, 255, 0)
YELLOW_GREEN = (154, 205)

File diff suppressed because it is too large Load Diff

View File

@ -1,113 +0,0 @@
"""
from: https://github.com/linuxlewis/tripy/blob/master/tripy.py
"""
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
def earclip(polygon):
"""
Simple earclipping algorithm for a given polygon p.
polygon is expected to be an array of 2-tuples of the cartesian points of the polygon
For a polygon with n points it will return n-2 triangles.
The triangles are returned as an array of 3-tuples where each item in the tuple is a 2-tuple of the cartesian point.
Implementation Reference:
- https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
"""
ear_vertex = []
triangles = []
polygon = [Point(*point) for point in polygon]
if _is_clockwise(polygon):
polygon.reverse()
point_count = len(polygon)
for i in range(point_count):
prev_index = i - 1
prev_point = polygon[prev_index]
point = polygon[i]
next_index = (i + 1) % point_count
next_point = polygon[next_index]
if _is_ear(prev_point, point, next_point, polygon):
ear_vertex.append(point)
while ear_vertex and point_count >= 3:
ear = ear_vertex.pop(0)
i = polygon.index(ear)
prev_index = i - 1
prev_point = polygon[prev_index]
next_index = (i + 1) % point_count
next_point = polygon[next_index]
polygon.remove(ear)
point_count -= 1
triangles.append(((prev_point.x, prev_point.y), (ear.x, ear.y), (next_point.x, next_point.y)))
if point_count > 3:
prev_prev_point = polygon[prev_index - 1]
next_next_index = (i + 1) % point_count
next_next_point = polygon[next_next_index]
groups = [
(prev_prev_point, prev_point, next_point, polygon),
(prev_point, next_point, next_next_point, polygon)
]
for group in groups:
p = group[1]
if _is_ear(*group):
if p not in ear_vertex:
ear_vertex.append(p)
elif p in ear_vertex:
ear_vertex.remove(p)
return triangles
def _is_clockwise(polygon):
s = 0
polygon_count = len(polygon)
for i in range(polygon_count):
point = polygon[i]
point2 = polygon[(i + 1) % polygon_count]
s += (point2.x - point.x) * (point2.y + point.y)
return s > 0
def _is_convex(prev, point, next_point):
return _triangle_sum(prev.x, prev.y, point.x, point.y, next_point.x, next_point.y) < 0
def _is_ear(p1, p2, p3, polygon):
ear = _contains_no_points(p1, p2, p3, polygon) and \
_is_convex(p1, p2, p3) and \
_triangle_area(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y) > 0
return ear
def _contains_no_points(p1, p2, p3, polygon):
for pn in polygon:
if pn in (p1, p2, p3):
continue
elif _is_point_inside(pn, p1, p2, p3):
return False
return True
def _is_point_inside(p, a, b, c):
area = _triangle_area(a.x, a.y, b.x, b.y, c.x, c.y)
area1 = _triangle_area(p.x, p.y, b.x, b.y, c.x, c.y)
area2 = _triangle_area(p.x, p.y, a.x, a.y, c.x, c.y)
area3 = _triangle_area(p.x, p.y, a.x, a.y, b.x, b.y)
return area == sum([area1, area2, area3])
def _triangle_area(x1, y1, x2, y2, x3, y3):
return abs((x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) / 2.0)
def _triangle_sum(x1, y1, x2, y2, x3, y3):
return x1 * (y3 - y2) + x2 * (y1 - y3) + x3 * (y2 - y1)

View File

@ -1,179 +0,0 @@
"""
Emitter - Invisible object that determines when Particles are emitted, actually emits them, and manages them
over their lifetime
"""
import arcade
from arcade.particle import Particle
from typing import Callable, cast
from arcade.utils import _Vec2
from arcade.arcade_types import Point, Vector
##########
class EmitController:
"""Base class for how a client configure the rate at which an Emitter emits Particles
Subclasses allow the client to control the rate and duration of emitting"""
def how_many(self, delta_time: float, current_particle_count: int) -> int:
raise NotImplemented("EmitterRate.how_many must be implemented")
def is_complete(self) -> bool:
raise NotImplemented("EmitterRate.is_complete must be implemented")
class EmitBurst(EmitController):
"""Used to configure an Emitter to emit particles in one burst"""
def __init__(self, count: int):
self._is_complete = False
self._count = count
def how_many(self, delta_time: float, current_particle_count: int) -> int:
if not self._is_complete:
self._is_complete = True
return self._count
return 0
def is_complete(self) -> bool:
return True
class EmitMaintainCount(EmitController):
"""Used to configure an Emitter so it emits particles so that the given count is always maintained"""
def __init__(self, particle_count: int):
self._target_count = particle_count
def how_many(self, delta_time: float, current_particle_count: int) -> int:
return self._target_count - current_particle_count
def is_complete(self) -> bool:
return False
class EmitInterval(EmitController):
"""Base class used to configure an Emitter to have a constant rate of emitting. Will emit indefinitely."""
def __init__(self, emit_interval: float):
self._emit_interval = emit_interval
self._carryover_time = 0.0
def how_many(self, delta_time: float, current_particle_count: int) -> int:
self._carryover_time += delta_time
emit_count = 0
while self._carryover_time >= self._emit_interval:
self._carryover_time -= self._emit_interval
emit_count += 1
return emit_count
def is_complete(self) -> bool:
return False
class EmitterIntervalWithCount(EmitInterval):
"""Configure an Emitter to emit particles with given interval, ending after emitting given number of particles"""
def __init__(self, emit_interval: float, particle_count: int):
super().__init__(emit_interval)
self._count_remaining = particle_count
def how_many(self, delta_time: float, current_particle_count: int) -> int:
proposed_count = super().how_many(delta_time, current_particle_count)
actual_count = min(proposed_count, self._count_remaining)
self._count_remaining -= actual_count
return actual_count
def is_complete(self) -> bool:
return self._count_remaining <= 0
class EmitterIntervalWithTime(EmitInterval):
"""Configure an Emitter to emit particles with given interval, ending after given number of seconds"""
def __init__(self, emit_interval: float, lifetime: float):
super().__init__(emit_interval)
self._lifetime = lifetime
def how_many(self, delta_time: float, current_particle_count: int) -> int:
if self._lifetime <= 0.0:
return 0
self._lifetime -= delta_time
return super().how_many(delta_time, current_particle_count)
def is_complete(self) -> bool:
return self._lifetime <= 0
# Emitter
class Emitter:
"""Emits and manages Particles over their lifetime. The foundational class in a particle system."""
def __init__(
self,
center_xy: Point,
emit_controller: EmitController,
particle_factory: Callable[["Emitter"], Particle],
change_xy: Vector = (0.0, 0.0),
emit_done_cb: Callable[["Emitter"], None] = None,
reap_cb: Callable[[], None] = None
):
# Note Self-reference with type annotations:
# https://www.python.org/dev/peps/pep-0484/#the-problem-of-forward-declarations
self.change_x = change_xy[0]
self.change_y = change_xy[1]
self.center_x = center_xy[0]
self.center_y = center_xy[1]
self.angle = 0.0
self.change_angle = 0.0
self.rate_factory = emit_controller
self.particle_factory = particle_factory
self._emit_done_cb = emit_done_cb
self._reap_cb = reap_cb
self._particles = arcade.SpriteList(use_spatial_hash=False)
def _emit(self):
"""Emit one particle, its initial position and velocity are relative to the position and angle of the emitter"""
p = self.particle_factory(self)
p.center_x += self.center_x
p.center_y += self.center_y
# given the velocity, rotate it by emitter's current angle
vel = _Vec2(p.change_x, p.change_y).rotated(self.angle)
p.change_x = vel.x
p.change_y = vel.y
self._particles.append(p)
def get_count(self):
return len(self._particles)
def get_pos(self) -> Point:
"""Get position of emitter"""
# TODO: should this be a property so a method call isn't needed?
return self.center_x, self.center_y
def update(self):
# update emitter
self.center_x += self.change_x
self.center_y += self.change_y
self.angle += self.change_angle
# update particles
emit_count = self.rate_factory.how_many(1 / 60, len(self._particles))
for _ in range(emit_count):
self._emit()
self._particles.update()
particles_to_reap = [p for p in self._particles if cast(Particle, p).can_reap()]
for dead_particle in particles_to_reap:
dead_particle.kill()
def draw(self):
self._particles.draw()
def can_reap(self):
"""Determine if Emitter can be deleted"""
is_emit_complete = self.rate_factory.is_complete()
can_reap = is_emit_complete and len(self._particles) <= 0
if is_emit_complete and self._emit_done_cb:
self._emit_done_cb(self)
self._emit_done_cb = None
if can_reap and self._reap_cb:
self._reap_cb()
self._reap_cb = None
return can_reap

View File

@ -1,62 +0,0 @@
"""
Convenience functions that provide a much simpler interface to Emitters and Particles.
These trade away some flexibility in favor of simplicity to allow beginners to start using particle systems.
"""
import arcade
import random
from typing import List
from arcade.arcade_types import Point
from arcade.particle import FilenameOrTexture
def make_burst_emitter(
center_xy: Point,
filenames_and_textures: List[FilenameOrTexture],
particle_count: int,
particle_speed: float,
particle_lifetime_min: float,
particle_lifetime_max: float,
particle_scale: float = 1.0,
fade_particles: bool = True):
"""Returns an emitter that emits all of its particles at once"""
particle_factory = arcade.LifetimeParticle
if fade_particles:
particle_factory = arcade.FadeParticle
return arcade.Emitter(
center_xy=center_xy,
emit_controller=arcade.EmitBurst(particle_count),
particle_factory=lambda emitter: particle_factory(
filename_or_texture=random.choice(filenames_and_textures),
change_xy=arcade.rand_in_circle((0.0, 0.0), particle_speed),
lifetime=random.uniform(particle_lifetime_min, particle_lifetime_max),
scale=particle_scale
)
)
def make_interval_emitter(
center_xy: Point,
filenames_and_textures: List[FilenameOrTexture],
emit_interval: float,
emit_duration: float,
particle_speed: float,
particle_lifetime_min: float,
particle_lifetime_max: float,
particle_scale: float = 1.0,
fade_particles: bool = True):
"""Returns an emitter that emits its particles at a constant rate for a given amount of time"""
particle_factory = arcade.LifetimeParticle
if fade_particles:
particle_factory = arcade.FadeParticle
return arcade.Emitter(
center_xy=center_xy,
emit_controller=arcade.EmitterIntervalWithTime(emit_interval, emit_duration),
particle_factory=lambda emitter: particle_factory(
filename_or_texture=random.choice(filenames_and_textures),
change_xy=arcade.rand_on_circle((0.0, 0.0), particle_speed),
lifetime=random.uniform(particle_lifetime_min, particle_lifetime_max),
scale=particle_scale
)
)

View File

@ -1,113 +0,0 @@
"""
Array Backed Grid
Show how to use a two-dimensional list/array to back the display of a
grid on-screen.
Note: Regular drawing commands are slow. Particularly when drawing a lot of
items, like the rectangles in this example.
For faster drawing, create the shapes and then draw them as a batch.
See array_backed_grid_buffered.py
If Python and Arcade are installed, this example can be run from the command line with:
python -m arcade.examples.array_backed_grid
"""
import arcade
# Set how many rows and columns we will have
ROW_COUNT = 15
COLUMN_COUNT = 15
# This sets the WIDTH and HEIGHT of each grid location
WIDTH = 30
HEIGHT = 30
# This sets the margin between each cell
# and on the edges of the screen.
MARGIN = 5
# Do the math to figure out our screen dimensions
SCREEN_WIDTH = (WIDTH + MARGIN) * COLUMN_COUNT + MARGIN
SCREEN_HEIGHT = (HEIGHT + MARGIN) * ROW_COUNT + MARGIN
SCREEN_TITLE = "Array Backed Grid Example"
class MyGame(arcade.Window):
"""
Main application class.
"""
def __init__(self, width, height, title):
"""
Set up the application.
"""
super().__init__(width, height, title)
# Create a 2 dimensional array. A two dimensional
# array is simply a list of lists.
self.grid = []
for row in range(ROW_COUNT):
# Add an empty array that will hold each cell
# in this row
self.grid.append([])
for column in range(COLUMN_COUNT):
self.grid[row].append(0) # Append a cell
arcade.set_background_color(arcade.color.BLACK)
def on_draw(self):
"""
Render the screen.
"""
# This command has to happen before we start drawing
arcade.start_render()
# Draw the grid
for row in range(ROW_COUNT):
for column in range(COLUMN_COUNT):
# Figure out what color to draw the box
if self.grid[row][column] == 1:
color = arcade.color.GREEN
else:
color = arcade.color.WHITE
# Do the math to figure out where the box is
x = (MARGIN + WIDTH) * column + MARGIN + WIDTH // 2
y = (MARGIN + HEIGHT) * row + MARGIN + HEIGHT // 2
# Draw the box
arcade.draw_rectangle_filled(x, y, WIDTH, HEIGHT, color)
def on_mouse_press(self, x, y, button, modifiers):
"""
Called when the user presses a mouse button.
"""
# Change the x/y screen coordinates to grid coordinates
column = x // (WIDTH + MARGIN)
row = y // (HEIGHT + MARGIN)
print(f"Click coordinates: ({x}, {y}). Grid coordinates: ({row}, {column})")
# Make sure we are on-grid. It is possible to click in the upper right
# corner in the margin and go to a grid location that doesn't exist
if row < ROW_COUNT and column < COLUMN_COUNT:
# Flip the location between 1 and 0.
if self.grid[row][column] == 0:
self.grid[row][column] = 1
else:
self.grid[row][column] = 0
def main():
MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
arcade.run()
if __name__ == "__main__":
main()

View File

@ -1,111 +0,0 @@
"""
Array Backed Grid
Show how to use a two-dimensional list/array to back the display of a
grid on-screen.
If Python and Arcade are installed, this example can be run from the command line with:
python -m arcade.examples.array_backed_grid_buffered
"""
import arcade
# Set how many rows and columns we will have
ROW_COUNT = 15
COLUMN_COUNT = 15
# This sets the WIDTH and HEIGHT of each grid location
WIDTH = 30
HEIGHT = 30
# This sets the margin between each cell
# and on the edges of the screen.
MARGIN = 5
# Do the math to figure out our screen dimensions
SCREEN_WIDTH = (WIDTH + MARGIN) * COLUMN_COUNT + MARGIN
SCREEN_HEIGHT = (HEIGHT + MARGIN) * ROW_COUNT + MARGIN
SCREEN_TITLE = "Array Backed Grid Buffered Example"
class MyGame(arcade.Window):
"""
Main application class.
"""
def __init__(self, width, height, title):
"""
Set up the application.
"""
super().__init__(width, height, title)
self.shape_list = None
# Create a 2 dimensional array. A two dimensional
# array is simply a list of lists.
self.grid = []
for row in range(ROW_COUNT):
# Add an empty array that will hold each cell
# in this row
self.grid.append([])
for column in range(COLUMN_COUNT):
self.grid[row].append(0) # Append a cell
arcade.set_background_color(arcade.color.BLACK)
self.recreate_grid()
def recreate_grid(self):
self.shape_list = arcade.ShapeElementList()
for row in range(ROW_COUNT):
for column in range(COLUMN_COUNT):
if self.grid[row][column] == 0:
color = arcade.color.WHITE
else:
color = arcade.color.GREEN
x = (MARGIN + WIDTH) * column + MARGIN + WIDTH // 2
y = (MARGIN + HEIGHT) * row + MARGIN + HEIGHT // 2
current_rect = arcade.create_rectangle_filled(x, y, WIDTH, HEIGHT, color)
self.shape_list.append(current_rect)
def on_draw(self):
"""
Render the screen.
"""
# This command has to happen before we start drawing
arcade.start_render()
self.shape_list.draw()
def on_mouse_press(self, x, y, button, modifiers):
"""
Called when the user presses a mouse button.
"""
# Change the x/y screen coordinates to grid coordinates
column = x // (WIDTH + MARGIN)
row = y // (HEIGHT + MARGIN)
print(f"Click coordinates: ({x}, {y}). Grid coordinates: ({row}, {column})")
# Make sure we are on-grid. It is possible to click in the upper right
# corner in the margin and go to a grid location that doesn't exist
if row < ROW_COUNT and column < COLUMN_COUNT:
# Flip the location between 1 and 0.
if self.grid[row][column] == 0:
self.grid[row][column] = 1
else:
self.grid[row][column] = 0
self.recreate_grid()
def main():
MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
arcade.run()
if __name__ == "__main__":
main()

View File

@ -1,392 +0,0 @@
"""
Asteroid Smasher
Shoot space rocks in this demo program created with
Python and the Arcade library.
Artwork from http://kenney.nl
If Python and Arcade are installed, this example can be run from the command line with:
python -m arcade.examples.asteroid_smasher
"""
import random
import math
import arcade
import os
STARTING_ASTEROID_COUNT = 3
SCALE = 0.5
OFFSCREEN_SPACE = 300
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
SCREEN_TITLE = "Asteroid Smasher"
LEFT_LIMIT = -OFFSCREEN_SPACE
RIGHT_LIMIT = SCREEN_WIDTH + OFFSCREEN_SPACE
BOTTOM_LIMIT = -OFFSCREEN_SPACE
TOP_LIMIT = SCREEN_HEIGHT + OFFSCREEN_SPACE
class TurningSprite(arcade.Sprite):
""" Sprite that sets its angle to the direction it is traveling in. """
def update(self):
super().update()
self.angle = math.degrees(math.atan2(self.change_y, self.change_x))
class ShipSprite(arcade.Sprite):
"""
Sprite that represents our space ship.
Derives from arcade.Sprite.
"""
def __init__(self, filename, scale):
""" Set up the space ship. """
# Call the parent Sprite constructor
super().__init__(filename, scale)
# Info on where we are going.
# Angle comes in automatically from the parent class.
self.thrust = 0
self.speed = 0
self.max_speed = 4
self.drag = 0.05
self.respawning = 0
# Mark that we are respawning.
self.respawn()
def respawn(self):
"""
Called when we die and need to make a new ship.
'respawning' is an invulnerability timer.
"""
# If we are in the middle of respawning, this is non-zero.
self.respawning = 1
self.center_x = SCREEN_WIDTH / 2
self.center_y = SCREEN_HEIGHT / 2
self.angle = 0
def update(self):
"""
Update our position and other particulars.
"""
if self.respawning:
self.respawning += 1
self.alpha = self.respawning
if self.respawning > 250:
self.respawning = 0
self.alpha = 255
if self.speed > 0:
self.speed -= self.drag
if self.speed < 0:
self.speed = 0
if self.speed < 0:
self.speed += self.drag
if self.speed > 0:
self.speed = 0
self.speed += self.thrust
if self.speed > self.max_speed:
self.speed = self.max_speed
if self.speed < -self.max_speed:
self.speed = -self.max_speed
self.change_x = -math.sin(math.radians(self.angle)) * self.speed
self.change_y = math.cos(math.radians(self.angle)) * self.speed
self.center_x += self.change_x
self.center_y += self.change_y
""" Call the parent class. """
super().update()
class AsteroidSprite(arcade.Sprite):
""" Sprite that represents an asteroid. """
def __init__(self, image_file_name, scale):
super().__init__(image_file_name, scale=scale)
self.size = 0
def update(self):
""" Move the asteroid around. """
super().update()
if self.center_x < LEFT_LIMIT:
self.center_x = RIGHT_LIMIT
if self.center_x > RIGHT_LIMIT:
self.center_x = LEFT_LIMIT
if self.center_y > TOP_LIMIT:
self.center_y = BOTTOM_LIMIT
if self.center_y < BOTTOM_LIMIT:
self.center_y = TOP_LIMIT
class BulletSprite(TurningSprite):
"""
Class that represents a bullet.
Derives from arcade.TurningSprite which is just a Sprite
that aligns to its direction.
"""
def update(self):
super().update()
if self.center_x < -100 or self.center_x > 1500 or \
self.center_y > 1100 or self.center_y < -100:
self.kill()
class MyGame(arcade.Window):
""" Main application class. """
def __init__(self):
super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
# Set the working directory (where we expect to find files) to the same
# directory this .py file is in. You can leave this out of your own
# code, but it is needed to easily run the examples using "python -m"
# as mentioned at the top of this program.
file_path = os.path.dirname(os.path.abspath(__file__))
os.chdir(file_path)
self.frame_count = 0
self.game_over = False
# Sprite lists
self.all_sprites_list = None
self.asteroid_list = None
self.bullet_list = None
self.ship_life_list = None
# Set up the player
self.score = 0
self.player_sprite = None
self.lives = 3
# Sounds
self.laser_sound = arcade.load_sound("sounds/laser1.wav")
def start_new_game(self):
""" Set up the game and initialize the variables. """
self.frame_count = 0
self.game_over = False
# Sprite lists
self.all_sprites_list = arcade.SpriteList()
self.asteroid_list = arcade.SpriteList()
self.bullet_list = arcade.SpriteList()
self.ship_life_list = arcade.SpriteList()
# Set up the player
self.score = 0
self.player_sprite = ShipSprite("images/playerShip1_orange.png", SCALE)
self.all_sprites_list.append(self.player_sprite)
self.lives = 3
# Set up the little icons that represent the player lives.
cur_pos = 10
for i in range(self.lives):
life = arcade.Sprite("images/playerLife1_orange.png", SCALE)
life.center_x = cur_pos + life.width
life.center_y = life.height
cur_pos += life.width
self.all_sprites_list.append(life)
self.ship_life_list.append(life)
# Make the asteroids
image_list = ("images/meteorGrey_big1.png",
"images/meteorGrey_big2.png",
"images/meteorGrey_big3.png",
"images/meteorGrey_big4.png")
for i in range(STARTING_ASTEROID_COUNT):
image_no = random.randrange(4)
enemy_sprite = AsteroidSprite(image_list[image_no], SCALE)
enemy_sprite.guid = "Asteroid"
enemy_sprite.center_y = random.randrange(BOTTOM_LIMIT, TOP_LIMIT)
enemy_sprite.center_x = random.randrange(LEFT_LIMIT, RIGHT_LIMIT)
enemy_sprite.change_x = random.random() * 2 - 1
enemy_sprite.change_y = random.random() * 2 - 1
enemy_sprite.change_angle = (random.random() - 0.5) * 2
enemy_sprite.size = 4
self.all_sprites_list.append(enemy_sprite)
self.asteroid_list.append(enemy_sprite)
def on_draw(self):
"""
Render the screen.
"""
# This command has to happen before we start drawing
arcade.start_render()
# Draw all the sprites.
self.all_sprites_list.draw()
# Put the text on the screen.
output = f"Score: {self.score}"
arcade.draw_text(output, 10, 70, arcade.color.WHITE, 13)
output = f"Asteroid Count: {len(self.asteroid_list)}"
arcade.draw_text(output, 10, 50, arcade.color.WHITE, 13)
def on_key_press(self, symbol, modifiers):
""" Called whenever a key is pressed. """
# Shoot if the player hit the space bar and we aren't respawning.
if not self.player_sprite.respawning and symbol == arcade.key.SPACE:
bullet_sprite = BulletSprite("images/laserBlue01.png", SCALE)
bullet_sprite.guid = "Bullet"
bullet_speed = 13
bullet_sprite.change_y = \
math.cos(math.radians(self.player_sprite.angle)) * bullet_speed
bullet_sprite.change_x = \
-math.sin(math.radians(self.player_sprite.angle)) \
* bullet_speed
bullet_sprite.center_x = self.player_sprite.center_x
bullet_sprite.center_y = self.player_sprite.center_y
bullet_sprite.update()
self.all_sprites_list.append(bullet_sprite)
self.bullet_list.append(bullet_sprite)
arcade.play_sound(self.laser_sound)
if symbol == arcade.key.LEFT:
self.player_sprite.change_angle = 3
elif symbol == arcade.key.RIGHT:
self.player_sprite.change_angle = -3
elif symbol == arcade.key.UP:
self.player_sprite.thrust = 0.15
elif symbol == arcade.key.DOWN:
self.player_sprite.thrust = -.2
def on_key_release(self, symbol, modifiers):
""" Called whenever a key is released. """
if symbol == arcade.key.LEFT:
self.player_sprite.change_angle = 0
elif symbol == arcade.key.RIGHT:
self.player_sprite.change_angle = 0
elif symbol == arcade.key.UP:
self.player_sprite.thrust = 0
elif symbol == arcade.key.DOWN:
self.player_sprite.thrust = 0
def split_asteroid(self, asteroid: AsteroidSprite):
""" Split an asteroid into chunks. """
x = asteroid.center_x
y = asteroid.center_y
self.score += 1
if asteroid.size == 4:
for i in range(3):
image_no = random.randrange(2)
image_list = ["images/meteorGrey_med1.png",
"images/meteorGrey_med2.png"]
enemy_sprite = AsteroidSprite(image_list[image_no],
SCALE * 1.5)
enemy_sprite.center_y = y
enemy_sprite.center_x = x
enemy_sprite.change_x = random.random() * 2.5 - 1.25
enemy_sprite.change_y = random.random() * 2.5 - 1.25
enemy_sprite.change_angle = (random.random() - 0.5) * 2
enemy_sprite.size = 3
self.all_sprites_list.append(enemy_sprite)
self.asteroid_list.append(enemy_sprite)
elif asteroid.size == 3:
for i in range(3):
image_no = random.randrange(2)
image_list = ["images/meteorGrey_small1.png",
"images/meteorGrey_small2.png"]
enemy_sprite = AsteroidSprite(image_list[image_no],
SCALE * 1.5)
enemy_sprite.center_y = y
enemy_sprite.center_x = x
enemy_sprite.change_x = random.random() * 3 - 1.5
enemy_sprite.change_y = random.random() * 3 - 1.5
enemy_sprite.change_angle = (random.random() - 0.5) * 2
enemy_sprite.size = 2
self.all_sprites_list.append(enemy_sprite)
self.asteroid_list.append(enemy_sprite)
elif asteroid.size == 2:
for i in range(3):
image_no = random.randrange(2)
image_list = ["images/meteorGrey_tiny1.png",
"images/meteorGrey_tiny2.png"]
enemy_sprite = AsteroidSprite(image_list[image_no],
SCALE * 1.5)
enemy_sprite.center_y = y
enemy_sprite.center_x = x
enemy_sprite.change_x = random.random() * 3.5 - 1.75
enemy_sprite.change_y = random.random() * 3.5 - 1.75
enemy_sprite.change_angle = (random.random() - 0.5) * 2
enemy_sprite.size = 1
self.all_sprites_list.append(enemy_sprite)
self.asteroid_list.append(enemy_sprite)
def update(self, x):
""" Move everything """
self.frame_count += 1
if not self.game_over:
self.all_sprites_list.update()
for bullet in self.bullet_list:
asteroids_plain = arcade.check_for_collision_with_list(bullet, self.asteroid_list)
asteroids_spatial = arcade.check_for_collision_with_list(bullet, self.asteroid_list)
if len(asteroids_plain) != len(asteroids_spatial):
print("ERROR")
asteroids = asteroids_spatial
for asteroid in asteroids:
self.split_asteroid(asteroid)
asteroid.kill()
bullet.kill()
if not self.player_sprite.respawning:
asteroids = arcade.check_for_collision_with_list(self.player_sprite, self.asteroid_list)
if len(asteroids) > 0:
if self.lives > 0:
self.lives -= 1
self.player_sprite.respawn()
self.split_asteroid(asteroids[0])
asteroids[0].kill()
self.ship_life_list.pop().kill()
print("Crash")
else:
self.game_over = True
print("Game over")
def main():
window = MyGame()
window.start_new_game()
arcade.run()
if __name__ == "__main__":
main()

View File

@ -1,97 +0,0 @@
"""
Bounce a ball on the screen, using gravity.
If Python and Arcade are installed, this example can be run from the command line with:
python -m arcade.examples.bouncing_ball
"""
import arcade
# --- Set up the constants
# Size of the screen
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 600
SCREEN_TITLE = "Bouncing Ball Example"
# Size of the circle.
CIRCLE_RADIUS = 20
# How strong the gravity is.
GRAVITY_CONSTANT = 0.3
# Percent of velocity maintained on a bounce.
BOUNCINESS = 0.9
def draw(delta_time):
"""
Use this function to draw everything to the screen.
"""
# Start the render. This must happen before any drawing
# commands. We do NOT need an stop render command.
arcade.start_render()
# Draw our rectangle
arcade.draw_circle_filled(draw.x, draw.y, CIRCLE_RADIUS,
arcade.color.BLACK)
# Modify rectangles position based on the delta
# vector. (Delta means change. You can also think
# of this as our speed and direction.)
draw.x += draw.delta_x
draw.y += draw.delta_y
draw.delta_y -= GRAVITY_CONSTANT
# Figure out if we hit the left or right edge and need to reverse.
if draw.x < CIRCLE_RADIUS and draw.delta_x < 0:
draw.delta_x *= -BOUNCINESS
elif draw.x > SCREEN_WIDTH - CIRCLE_RADIUS and draw.delta_x > 0:
draw.delta_x *= -BOUNCINESS
# See if we hit the bottom
if draw.y < CIRCLE_RADIUS and draw.delta_y < 0:
# If we bounce with a decent velocity, do a normal bounce.
# Otherwise we won't have enough time resolution to accurate represent
# the bounce and it will bounce forever. So we'll divide the bounciness
# by half to let it settle out.
if draw.delta_y * -1 > GRAVITY_CONSTANT * 15:
draw.delta_y *= -BOUNCINESS
else:
draw.delta_y *= -BOUNCINESS / 2
# Below are function-specific variables. Before we use them
# in our function, we need to give them initial values. Then
# the values will persist between function calls.
#
# In other languages, we'd declare the variables as 'static' inside the
# function to get that same functionality.
#
# Later on, we'll use 'classes' to track position and velocity for multiple
# objects.
draw.x = CIRCLE_RADIUS
draw.y = SCREEN_HEIGHT - CIRCLE_RADIUS
draw.delta_x = 2
draw.delta_y = 0
def main():
# Open up our window
arcade.open_window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
arcade.set_background_color(arcade.color.WHITE)
# Tell the computer to call the draw command at the specified interval.
arcade.schedule(draw, 1 / 80)
# Run the program
arcade.run()
# When done running the program, close the window.
arcade.close_window()
if __name__ == "__main__":
main()

View File

@ -1,113 +0,0 @@
"""
Bounce balls on the screen.
Spawn a new ball for each mouse-click.
If Python and Arcade are installed, this example can be run from the command line with:
python -m arcade.examples.bouncing_balls
"""
import arcade
import random
# --- Set up the constants
# Size of the screen
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 600
SCREEN_TITLE = "Bouncing Balls Example"
class Ball:
"""
Class to keep track of a ball's location and vector.
"""
def __init__(self):
self.x = 0
self.y = 0
self.change_x = 0
self.change_y = 0
self.size = 0
self.color = None
def make_ball():
"""
Function to make a new, random ball.
"""
ball = Ball()
# Size of the ball
ball.size = random.randrange(10, 30)
# Starting position of the ball.
# Take into account the ball size so we don't spawn on the edge.
ball.x = random.randrange(ball.size, SCREEN_WIDTH - ball.size)
ball.y = random.randrange(ball.size, SCREEN_HEIGHT - ball.size)
# Speed and direction of rectangle
ball.change_x = random.randrange(-2, 3)
ball.change_y = random.randrange(-2, 3)
# Color
ball.color = (random.randrange(256), random.randrange(256), random.randrange(256))
return ball
class MyGame(arcade.Window):
""" Main application class. """
def __init__(self):
super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
self.ball_list = []
ball = make_ball()
self.ball_list.append(ball)
def on_draw(self):
"""
Render the screen.
"""
# This command has to happen before we start drawing
arcade.start_render()
for ball in self.ball_list:
arcade.draw_circle_filled(ball.x, ball.y, ball.size, ball.color)
# Put the text on the screen.
output = "Balls: {}".format(len(self.ball_list))
arcade.draw_text(output, 10, 20, arcade.color.WHITE, 14)
def update(self, delta_time):
""" Movement and game logic """
for ball in self.ball_list:
ball.x += ball.change_x
ball.y += ball.change_y
if ball.x < ball.size:
ball.change_x *= -1
if ball.y < ball.size:
ball.change_y *= -1
if ball.x > SCREEN_WIDTH - ball.size:
ball.change_x *= -1
if ball.y > SCREEN_HEIGHT - ball.size:
ball.change_y *= -1
def on_mouse_press(self, x, y, button, modifiers):
"""
Called whenever the mouse button is clicked.
"""
ball = make_ball()
self.ball_list.append(ball)
def main():
MyGame()
arcade.run()
if __name__ == "__main__":
main()

View File

@ -1,95 +0,0 @@
"""
This simple animation example shows how to bounce a rectangle
on the screen.
It assumes a programmer knows how to create functions already.
It does not assume a programmer knows how to create classes. If you do know
how to create classes, see the starting template for a better example:
http://arcade.academy/examples/starting_template.html
Or look through the examples showing how to use Sprites.
A video walk-through of this example is available at:
https://vimeo.com/168063840
If Python and Arcade are installed, this example can be run from the command line with:
python -m arcade.examples.bouncing_rectangle
"""
import arcade
# --- Set up the constants
# Size of the screen
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 600
SCREEN_TITLE = "Bouncing Rectangle Example"
# Size of the rectangle
RECT_WIDTH = 50
RECT_HEIGHT = 50
def on_draw(delta_time):
"""
Use this function to draw everything to the screen.
"""
# Start the render. This must happen before any drawing
# commands. We do NOT need a stop render command.
arcade.start_render()
# Draw a rectangle.
# For a full list of colors see:
# http://arcade.academy/arcade.color.html
arcade.draw_rectangle_filled(on_draw.center_x, on_draw.center_y,
RECT_WIDTH, RECT_HEIGHT,
arcade.color.ALIZARIN_CRIMSON)
# Modify rectangles position based on the delta
# vector. (Delta means change. You can also think
# of this as our speed and direction.)
on_draw.center_x += on_draw.delta_x * delta_time
on_draw.center_y += on_draw.delta_y * delta_time
# Figure out if we hit the edge and need to reverse.
if on_draw.center_x < RECT_WIDTH // 2 \
or on_draw.center_x > SCREEN_WIDTH - RECT_WIDTH // 2:
on_draw.delta_x *= -1
if on_draw.center_y < RECT_HEIGHT // 2 \
or on_draw.center_y > SCREEN_HEIGHT - RECT_HEIGHT // 2:
on_draw.delta_y *= -1
# Below are function-specific variables. Before we use them
# in our function, we need to give them initial values. Then
# the values will persist between function calls.
#
# In other languages, we'd declare the variables as 'static' inside the
# function to get that same functionality.
#
# Later on, we'll use 'classes' to track position and velocity for multiple
# objects.
on_draw.center_x = 100 # Initial x position
on_draw.center_y = 50 # Initial y position
on_draw.delta_x = 115 # Initial change in x
on_draw.delta_y = 130 # Initial change in y
def main():
# Open up our window
arcade.open_window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
arcade.set_background_color(arcade.color.WHITE)
# Tell the computer to call the draw command at the specified interval.
arcade.schedule(on_draw, 1 / 80)
# Run the program
arcade.run()
if __name__ == "__main__":
main()

View File

@ -1,124 +0,0 @@
"""
Example "Arcade" library code.
If Python and Arcade are installed, this example can be run from the command line with:
python -m arcade.examples.decorator_drawing_example
"""
# Library imports
import arcade
import random
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
SCREEN_TITLE = "Drawing With Decorators Example"
window = arcade.open_window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
bird_list = []
def setup():
create_birds()
arcade.schedule(update, 1 / 60)
arcade.run()
def create_birds():
for bird_count in range(10):
x = random.randrange(SCREEN_WIDTH)
y = random.randrange(SCREEN_HEIGHT/2, SCREEN_HEIGHT)
bird_list.append([x, y])
def update(delta_time):
"""
This is run every 1/60 of a second or so. Do not draw anything
in this function.
"""
change_y = 0.3
for bird in bird_list:
bird[0] += change_y
if bird[0] > SCREEN_WIDTH + 20:
bird[0] = -20
@window.event
def on_draw():
"""
This is called every time we need to update our screen. About 60
times per second.
Just draw things in this function, don't update where they are.
"""
# Call our drawing functions.
draw_background()
draw_birds()
draw_trees()
def draw_background():
"""
This function draws the background. Specifically, the sky and ground.
"""
# Draw the sky in the top two-thirds
arcade.draw_rectangle_filled(SCREEN_WIDTH / 2, SCREEN_HEIGHT * 2 / 3,
SCREEN_WIDTH - 1, SCREEN_HEIGHT * 2 / 3,
arcade.color.SKY_BLUE)
# Draw the ground in the bottom third
arcade.draw_rectangle_filled(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 6,
SCREEN_WIDTH - 1, SCREEN_HEIGHT / 3,
arcade.color.DARK_SPRING_GREEN)
def draw_birds():
for bird in bird_list:
# Draw the bird.
draw_bird(bird[0], bird[1])
def draw_bird(x, y):
"""
Draw a bird using a couple arcs.
"""
arcade.draw_arc_outline(x, y, 20, 20, arcade.color.BLACK, 0, 90)
arcade.draw_arc_outline(x + 40, y, 20, 20, arcade.color.BLACK, 90, 180)
def draw_trees():
# Draw the top row of trees
for x in range(45, SCREEN_WIDTH, 90):
draw_pine_tree(x, SCREEN_HEIGHT / 3)
# Draw the bottom row of trees
for x in range(65, SCREEN_WIDTH, 90):
draw_pine_tree(x, (SCREEN_HEIGHT / 3) - 120)
def draw_pine_tree(center_x, center_y):
"""
This function draws a pine tree at the specified location.
Args:
:center_x: x position of the tree center.
:center_y: y position of the tree trunk center.
"""
# Draw the trunk center_x
arcade.draw_rectangle_filled(center_x, center_y, 20, 40, arcade.color.DARK_BROWN)
tree_bottom_y = center_y + 20
# Draw the triangle on top of the trunk
point_list = ((center_x - 40, tree_bottom_y),
(center_x, tree_bottom_y + 100),
(center_x + 40, tree_bottom_y))
arcade.draw_polygon_filled(point_list, arcade.color.DARK_GREEN)
if __name__ == "__main__":
setup()

View File

@ -1,168 +0,0 @@
"""
Example "Arcade" library code.
This example shows the drawing primitives and how they are used.
It does not assume the programmer knows how to define functions or classes
yet.
API documentation for the draw commands can be found here:
http://arcade.academy/quick_index.html#id1
A video explaining this example can be found here:
https://vimeo.com/167158158
If Python and Arcade are installed, this example can be run from the command line with:
python -m arcade.examples.drawing_primitives
"""
# Import the Arcade library. If this fails, then try following the instructions
# for how to install arcade:
# http://arcade.academy/installation.html
import arcade
import os
# Set the working directory (where we expect to find files) to the same
# directory this .py file is in. You can leave this out of your own
# code, but it is needed to easily run the examples using "python -m"
# as mentioned at the top of this program.
file_path = os.path.dirname(os.path.abspath(__file__))
os.chdir(file_path)
# Open the window. Set the window title and dimensions (width and height)
arcade.open_window(600, 600, "Drawing Primitives Example")
# Set the background color to white
# For a list of named colors see
# http://arcade.academy/arcade.color.html
# Colors can also be specified in (red, green, blue) format and
# (red, green, blue, alpha) format.
arcade.set_background_color(arcade.color.WHITE)
# Start the render process. This must be done before any drawing commands.
arcade.start_render()
# Draw a grid
# Draw vertical lines every 120 pixels
for x in range(0, 601, 120):
arcade.draw_line(x, 0, x, 600, arcade.color.BLACK, 2)
# Draw horizontal lines every 200 pixels
for y in range(0, 601, 200):
arcade.draw_line(0, y, 800, y, arcade.color.BLACK, 2)
# Draw a point
arcade.draw_text("draw_point", 3, 405, arcade.color.BLACK, 12)
arcade.draw_point(60, 495, arcade.color.RED, 10)
# Draw a set of points
arcade.draw_text("draw_points", 123, 405, arcade.color.BLACK, 12)
point_list = ((165, 495),
(165, 480),
(165, 465),
(195, 495),
(195, 480),
(195, 465))
arcade.draw_points(point_list, arcade.color.ZAFFRE, 10)
# Draw a line
arcade.draw_text("draw_line", 243, 405, arcade.color.BLACK, 12)
arcade.draw_line(270, 495, 300, 450, arcade.color.WOOD_BROWN, 3)
# Draw a set of lines
arcade.draw_text("draw_lines", 363, 405, arcade.color.BLACK, 12)
point_list = ((390, 450),
(450, 450),
(390, 480),
(450, 480),
(390, 510),
(450, 510)
)
arcade.draw_lines(point_list, arcade.color.BLUE, 3)
# Draw a line strip
arcade.draw_text("draw_line_strip", 483, 405, arcade.color.BLACK, 12)
point_list = ((510, 450),
(570, 450),
(510, 480),
(570, 480),
(510, 510),
(570, 510)
)
arcade.draw_line_strip(point_list, arcade.color.TROPICAL_RAIN_FOREST, 3)
# Draw a polygon
arcade.draw_text("draw_polygon_outline", 3, 207, arcade.color.BLACK, 9)
point_list = ((30, 240),
(45, 240),
(60, 255),
(60, 285),
(45, 300),
(30, 300))
arcade.draw_polygon_outline(point_list, arcade.color.SPANISH_VIOLET, 3)
# Draw a filled in polygon
arcade.draw_text("draw_polygon_filled", 123, 207, arcade.color.BLACK, 9)
point_list = ((150, 240),
(165, 240),
(180, 255),
(180, 285),
(165, 300),
(150, 300))
arcade.draw_polygon_filled(point_list, arcade.color.SPANISH_VIOLET)
# Draw an outline of a circle
arcade.draw_text("draw_circle_outline", 243, 207, arcade.color.BLACK, 10)
arcade.draw_circle_outline(300, 285, 18, arcade.color.WISTERIA, 3)
# Draw a filled in circle
arcade.draw_text("draw_circle_filled", 363, 207, arcade.color.BLACK, 10)
arcade.draw_circle_filled(420, 285, 18, arcade.color.GREEN)
# Draw an ellipse outline, and another one rotated
arcade.draw_text("draw_ellipse_outline", 483, 207, arcade.color.BLACK, 10)
arcade.draw_ellipse_outline(540, 273, 15, 36, arcade.color.AMBER, 3)
arcade.draw_ellipse_outline(540, 336, 15, 36,
arcade.color.BLACK_BEAN, 3, 45)
# Draw a filled ellipse, and another one rotated
arcade.draw_text("draw_ellipse_filled", 3, 3, arcade.color.BLACK, 10)
arcade.draw_ellipse_filled(60, 81, 15, 36, arcade.color.AMBER)
arcade.draw_ellipse_filled(60, 144, 15, 36,
arcade.color.BLACK_BEAN, 45)
# Draw an arc, and another one rotated
arcade.draw_text("draw_arc/filled_arc", 123, 3, arcade.color.BLACK, 10)
arcade.draw_arc_outline(150, 81, 15, 36,
arcade.color.BRIGHT_MAROON, 90, 360)
arcade.draw_arc_filled(150, 144, 15, 36,
arcade.color.BOTTLE_GREEN, 90, 360, 45)
# Draw an rectangle outline
arcade.draw_text("draw_rect", 243, 3, arcade.color.BLACK, 10)
arcade.draw_rectangle_outline(295, 100, 45, 65,
arcade.color.BRITISH_RACING_GREEN)
arcade.draw_rectangle_outline(295, 160, 20, 45,
arcade.color.BRITISH_RACING_GREEN, 3, 45)
# Draw a filled in rectangle
arcade.draw_text("draw_filled_rect", 363, 3, arcade.color.BLACK, 10)
arcade.draw_rectangle_filled(420, 100, 45, 65, arcade.color.BLUSH)
arcade.draw_rectangle_filled(420, 160, 20, 40, arcade.color.BLUSH, 45)
# Load and draw an image to the screen
# Image from kenney.nl asset pack #1
arcade.draw_text("draw_bitmap", 483, 3, arcade.color.BLACK, 12)
texture = arcade.load_texture("images/playerShip1_orange.png")
scale = .6
arcade.draw_texture_rectangle(540, 120, scale * texture.width,
scale * texture.height, texture, 0)
arcade.draw_texture_rectangle(540, 60, scale * texture.width,
scale * texture.height, texture, 45)
# Finish the render.
# Nothing will be drawn without this.
# Must happen after all draw commands
arcade.finish_render()
# Keep the window up until someone closes it.
arcade.run()

View File

@ -1,107 +0,0 @@
"""
Example showing how to draw text to the screen.
If Python and Arcade are installed, this example can be run from the command line with:
python -m arcade.examples.drawing_text
"""
import arcade
SCREEN_WIDTH = 500
SCREEN_HEIGHT = 500
SCREEN_TITLE = "Drawing Text Example"
class MyGame(arcade.Window):
"""
Main application class.
"""
def __init__(self, width, height, title):
super().__init__(width, height, title)
arcade.set_background_color(arcade.color.WHITE)
self.text_angle = 0
self.time_elapsed = 0.0
def update(self, delta_time):
self.text_angle += 1
self.time_elapsed += delta_time
def on_draw(self):
"""
Render the screen.
"""
# This command should happen before we start drawing. It will clear
# the screen to the background color, and erase what we drew last frame.
arcade.start_render()
# start_x and start_y make the start point for the text. We draw a dot to make it easy too see
# the text in relation to its start x and y.
start_x = 50
start_y = 450
arcade.draw_point(start_x, start_y, arcade.color.BLUE, 5)
arcade.draw_text("Simple line of text in 12 point", start_x, start_y, arcade.color.BLACK, 12)
start_x = 50
start_y = 150
arcade.draw_point(start_x, start_y, arcade.color.BLUE, 5)
arcade.draw_text("Garamond Text", start_x, start_y, arcade.color.BLACK, 15, font_name='GARA')
start_x = 50
start_y = 400
arcade.draw_point(start_x, start_y, arcade.color.BLUE, 5)
arcade.draw_text("Text anchored 'top' and 'left'.",
start_x, start_y, arcade.color.BLACK, 12, anchor_x="left", anchor_y="top")
start_y = 350
arcade.draw_point(start_x, start_y, arcade.color.BLUE, 5)
arcade.draw_text("14 point multi\nline\ntext",
start_x, start_y, arcade.color.BLACK, 14, anchor_y="top")
start_y = 450
start_x = 300
width = 200
height = 20
arcade.draw_point(start_x, start_y, arcade.color.BLUE, 5)
arcade.draw_lrtb_rectangle_outline(start_x, start_x + width,
start_y + height, start_y,
arcade.color.BLUE, 1)
arcade.draw_text("Centered Text.",
start_x, start_y, arcade.color.BLACK, 14, width=200, align="center")
start_y = 250
start_x = 300
arcade.draw_point(start_x, start_y, arcade.color.BLUE, 5)
arcade.draw_text("Text centered on\na point",
start_x, start_y, arcade.color.BLACK, 14, width=200, align="center",
anchor_x="center", anchor_y="center")
start_y = 150
start_x = 300
arcade.draw_point(start_x, start_y, arcade.color.BLUE, 5)
arcade.draw_text("Text rotated on\na point", start_x, start_y,
arcade.color.BLACK, 14, width=200, align="center", anchor_x="center",
anchor_y="center", rotation=self.text_angle)
start_y = 150
start_x = 20
arcade.draw_point(start_x, start_y, arcade.color.BLUE, 5)
arcade.draw_text("Sideways text", start_x, start_y,
arcade.color.BLACK, 14, width=200, align="center",
anchor_x="center", anchor_y="center", rotation=90.0)
start_y = 20
start_x = 50
arcade.draw_point(start_x, start_y, arcade.color.BLUE, 5)
arcade.draw_text(f"Time elapsed: {self.time_elapsed:7.1f}",
start_x, start_y, arcade.color.BLACK, 14)
def main():
MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
arcade.run()
if __name__ == "__main__":
main()

View File

@ -1,93 +0,0 @@
"""
Example "Arcade" library code.
This example shows how to use functions to draw a scene.
It does not assume that the programmer knows how to use classes yet.
A video walk-through of this code is available at:
https://vimeo.com/167296062
If Python and Arcade are installed, this example can be run from the command line with:
python -m arcade.examples.drawing_with_functions
"""
# Library imports
import arcade
# Constants - variables that do not change
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 600
SCREEN_TITLE = "Drawing With Functions Example"
def draw_background():
"""
This function draws the background. Specifically, the sky and ground.
"""
# Draw the sky in the top two-thirds
arcade.draw_lrtb_rectangle_filled(0,
SCREEN_WIDTH,
SCREEN_HEIGHT,
SCREEN_HEIGHT * (1 / 3),
arcade.color.SKY_BLUE)
# Draw the ground in the bottom third
arcade.draw_lrtb_rectangle_filled(0,
SCREEN_WIDTH,
SCREEN_HEIGHT / 3,
0,
arcade.color.DARK_SPRING_GREEN)
def draw_bird(x, y):
"""
Draw a bird using a couple arcs.
"""
arcade.draw_arc_outline(x, y, 20, 20, arcade.color.BLACK, 0, 90)
arcade.draw_arc_outline(x + 40, y, 20, 20, arcade.color.BLACK, 90, 180)
def draw_pine_tree(x, y):
"""
This function draws a pine tree at the specified location.
"""
# Draw the triangle on top of the trunk
arcade.draw_triangle_filled(x + 40, y,
x, y - 100,
x + 80, y - 100,
arcade.color.DARK_GREEN)
# Draw the trunk
arcade.draw_lrtb_rectangle_filled(x + 30, x + 50, y - 100, y - 140,
arcade.color.DARK_BROWN)
def main():
"""
This is the main program.
"""
# Open the window
arcade.open_window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
# Start the render process. This must be done before any drawing commands.
arcade.start_render()
# Call our drawing functions.
draw_background()
draw_pine_tree(50, 250)
draw_pine_tree(350, 320)
draw_bird(70, 500)
draw_bird(470, 550)
# Finish the render.
# Nothing will be drawn without this.
# Must happen after all draw commands
arcade.finish_render()
# Keep the window up until someone closes it.
arcade.run()
if __name__ == "__main__":
main()

View File

@ -1,109 +0,0 @@
"""
Example "Arcade" library code.
This example shows how to use functions and loops to draw a scene.
It does not assume that the programmer knows how to use classes yet.
If Python and Arcade are installed, this example can be run from the command line with:
python -m arcade.examples.drawing_with_loops
"""
# Library imports
import arcade
import random
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
SCREEN_TITLE = "Drawing With Loops Example"
def draw_background():
"""
This function draws the background. Specifically, the sky and ground.
"""
# Draw the sky in the top two-thirds
arcade.draw_rectangle_filled(SCREEN_WIDTH / 2, SCREEN_HEIGHT * 2 / 3,
SCREEN_WIDTH - 1, SCREEN_HEIGHT * 2 / 3,
arcade.color.SKY_BLUE)
# Draw the ground in the bottom third
arcade.draw_rectangle_filled(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 6,
SCREEN_WIDTH - 1, SCREEN_HEIGHT / 3,
arcade.color.DARK_SPRING_GREEN)
def draw_bird(x, y):
"""
Draw a bird using a couple arcs.
"""
arcade.draw_arc_outline(x, y, 20, 20, arcade.color.BLACK, 0, 90)
arcade.draw_arc_outline(x + 40, y, 20, 20, arcade.color.BLACK, 90, 180)
def draw_pine_tree(center_x, center_y):
"""
This function draws a pine tree at the specified location.
Args:
:center_x: x position of the tree center.
:center_y: y position of the tree trunk center.
"""
# Draw the trunkcenter_x
arcade.draw_rectangle_filled(center_x, center_y, 20, 40,
arcade.color.DARK_BROWN)
tree_bottom_y = center_y + 20
# Draw the triangle on top of the trunk
point_list = ((center_x - 40, tree_bottom_y),
(center_x, tree_bottom_y + 100),
(center_x + 40, tree_bottom_y))
arcade.draw_polygon_filled(point_list, arcade.color.DARK_GREEN)
def main():
"""
This is the main program.
"""
# Open the window
arcade.open_window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
# Start the render process. This must be done before any drawing commands.
arcade.start_render()
# Call our drawing functions.
draw_background()
# Loop to draw ten birds in random locations.
for bird_count in range(10):
# Any random x from 0 to the width of the screen
x = random.randrange(0, SCREEN_WIDTH)
# Any random y from in the top 2/3 of the screen.
# No birds on the ground.
y = random.randrange(SCREEN_HEIGHT / 3, SCREEN_HEIGHT - 20)
# Draw the bird.
draw_bird(x, y)
# Draw the top row of trees
for x in range(45, SCREEN_WIDTH, 90):
draw_pine_tree(x, SCREEN_HEIGHT / 3)
# Draw the bottom row of trees
for x in range(65, SCREEN_WIDTH, 90):
draw_pine_tree(x, (SCREEN_HEIGHT / 3) - 120)
# Finish the render.
# Nothing will be drawn without this.
# Must happen after all draw commands
arcade.finish_render()
# Keep the window up until someone closes it.
arcade.run()
if __name__ == "__main__":
main()

View File

@ -1,310 +0,0 @@
"""
Dual-stick Shooter Example
A dual-analog stick joystick is the preferred method of input. If a joystick is
not present, the game will fail back to use keyboard controls (WASD to move, arrows to shoot)
If Python and Arcade are installed, this example can be run from the command line with:
python -m arcade.examples.dual_stick_shooter
"""
import arcade
import random
import math
import os
from typing import cast
import pprint
SCREEN_WIDTH = 1024
SCREEN_HEIGHT = 768
SCREEN_TITLE = "Dual-stick Shooter Example"
MOVEMENT_SPEED = 4
BULLET_SPEED = 10
BULLET_COOLDOWN_TICKS = 10
ENEMY_SPAWN_INTERVAL = 1
ENEMY_SPEED = 1
JOY_DEADZONE = 0.2
def dump_obj(obj):
for key in sorted(vars(obj)):
val = getattr(obj, key)
print("{:30} = {} ({})".format(key, val, type(val).__name__))
def dump_joystick(joy):
print("========== {}".format(joy))
print("x {}".format(joy.x))
print("y {}".format(joy.y))
print("z {}".format(joy.z))
print("rx {}".format(joy.rx))
print("ry {}".format(joy.ry))
print("rz {}".format(joy.rz))
print("hat_x {}".format(joy.hat_x))
print("hat_y {}".format(joy.hat_y))
print("buttons {}".format(joy.buttons))
print("========== Extra joy")
dump_obj(joy)
print("========== Extra joy.device")
dump_obj(joy.device)
print("========== pprint joy")
pprint.pprint(joy)
print("========== pprint joy.device")
pprint.pprint(joy.device)
def dump_joystick_state(ticks, joy):
# print("{:5.2f} {:5.2f} {:>20} {:5}_".format(1.234567, -8.2757272903, "hello", str(True)))
fmt_str = "{:6d} "
num_fmts = ["{:5.2f}"] * 6
fmt_str += " ".join(num_fmts)
fmt_str += " {:2d} {:2d} {}"
buttons = " ".join(["{:5}".format(str(b)) for b in joy.buttons])
print(fmt_str.format(ticks,
joy.x,
joy.y,
joy.z,
joy.rx,
joy.ry,
joy.rz,
joy.hat_x,
joy.hat_y,
buttons))
def get_joy_position(x, y):
"""Given position of joystick axes, return (x, y, angle_in_degrees).
If movement is not outside of deadzone, return (None, None, None)"""
if x > JOY_DEADZONE or x < -JOY_DEADZONE or y > JOY_DEADZONE or y < -JOY_DEADZONE:
y = -y
rad = math.atan2(y, x)
angle = math.degrees(rad)
return x, y, angle
return None, None, None
class Player(arcade.sprite.Sprite):
def __init__(self, filename):
super().__init__(filename=filename, scale=0.4, center_x=SCREEN_WIDTH/2, center_y=SCREEN_HEIGHT/2)
self.shoot_up_pressed = False
self.shoot_down_pressed = False
self.shoot_left_pressed = False
self.shoot_right_pressed = False
class Enemy(arcade.sprite.Sprite):
def __init__(self, x, y):
super().__init__(filename='images/bumper.png', scale=0.5, center_x=x, center_y=y)
def follow_sprite(self, player_sprite):
"""
This function will move the current sprite towards whatever
other sprite is specified as a parameter.
We use the 'min' function here to get the sprite to line up with
the target sprite, and not jump around if the sprite is not off
an exact multiple of ENEMY_SPEED.
"""
if self.center_y < player_sprite.center_y:
self.center_y += min(ENEMY_SPEED, player_sprite.center_y - self.center_y)
elif self.center_y > player_sprite.center_y:
self.center_y -= min(ENEMY_SPEED, self.center_y - player_sprite.center_y)
if self.center_x < player_sprite.center_x:
self.center_x += min(ENEMY_SPEED, player_sprite.center_x - self.center_x)
elif self.center_x > player_sprite.center_x:
self.center_x -= min(ENEMY_SPEED, self.center_x - player_sprite.center_x)
class MyGame(arcade.Window):
def __init__(self, width, height, title):
super().__init__(width, height, title)
# Set the working directory (where we expect to find files) to the same
# directory this .py file is in. You can leave this out of your own
# code, but it is needed to easily run the examples using "python -m"
# as mentioned at the top of this program.
file_path = os.path.dirname(os.path.abspath(__file__))
os.chdir(file_path)
arcade.set_background_color(arcade.color.DARK_MIDNIGHT_BLUE)
self.game_over = False
self.score = 0
self.tick = 0
self.bullet_cooldown = 0
self.player = Player("images/playerShip2_orange.png")
self.bullet_list = arcade.SpriteList()
self.enemy_list = arcade.SpriteList()
self.joy = None
joys = arcade.get_joysticks()
for joy in joys:
dump_joystick(joy)
if joys:
self.joy = joys[0]
self.joy.open()
print("Using joystick controls: {}".format(self.joy.device))
arcade.window_commands.schedule(self.debug_joy_state, 0.1)
if not self.joy:
print("No joystick present, using keyboard controls")
arcade.window_commands.schedule(self.spawn_enemy, ENEMY_SPAWN_INTERVAL)
def debug_joy_state(self, delta_time):
dump_joystick_state(self.tick, self.joy)
def spawn_enemy(self, elapsed):
if self.game_over:
return
x = random.randint(0, SCREEN_WIDTH)
y = random.randint(0, SCREEN_HEIGHT)
self.enemy_list.append(Enemy(x, y))
def update(self, delta_time):
self.tick += 1
if self.game_over:
return
self.bullet_cooldown += 1
for enemy in self.enemy_list:
cast(Enemy, enemy).follow_sprite(self.player)
if self.joy:
# Joystick input - movement
move_x, move_y, move_angle = get_joy_position(self.joy.x, self.joy.y)
if move_angle:
self.player.change_x = move_x * MOVEMENT_SPEED
self.player.change_y = move_y * MOVEMENT_SPEED
# An angle of "0" means "right", but the player's image is drawn in the "up" direction.
# So an offset is needed.
self.player.angle = move_angle - 90
else:
self.player.change_x = 0
self.player.change_y = 0
# Joystick input - shooting
shoot_x, shoot_y, shoot_angle = get_joy_position(self.joy.z, self.joy.rz)
if shoot_angle:
self.spawn_bullet(shoot_angle)
else:
# Keyboard input - shooting
if self.player.shoot_right_pressed and self.player.shoot_up_pressed:
self.spawn_bullet(0+45)
elif self.player.shoot_up_pressed and self.player.shoot_left_pressed:
self.spawn_bullet(90+45)
elif self.player.shoot_left_pressed and self.player.shoot_down_pressed:
self.spawn_bullet(180+45)
elif self.player.shoot_down_pressed and self.player.shoot_right_pressed:
self.spawn_bullet(270+45)
elif self.player.shoot_right_pressed:
self.spawn_bullet(0)
elif self.player.shoot_up_pressed:
self.spawn_bullet(90)
elif self.player.shoot_left_pressed:
self.spawn_bullet(180)
elif self.player.shoot_down_pressed:
self.spawn_bullet(270)
self.enemy_list.update()
self.player.update()
self.bullet_list.update()
ship_death_hit_list = arcade.check_for_collision_with_list(self.player, self.enemy_list)
if len(ship_death_hit_list) > 0:
self.game_over = True
for bullet in self.bullet_list:
bullet_killed = False
enemy_shot_list = arcade.check_for_collision_with_list(bullet, self.enemy_list)
# Loop through each colliding sprite, remove it, and add to the score.
for enemy in enemy_shot_list:
enemy.kill()
bullet.kill()
bullet_killed = True
self.score += 1
if bullet_killed:
continue
def on_key_press(self, key, modifiers):
if key == arcade.key.W:
self.player.change_y = MOVEMENT_SPEED
self.player.angle = 0
elif key == arcade.key.A:
self.player.change_x = -MOVEMENT_SPEED
self.player.angle = 90
elif key == arcade.key.S:
self.player.change_y = -MOVEMENT_SPEED
self.player.angle = 180
elif key == arcade.key.D:
self.player.change_x = MOVEMENT_SPEED
self.player.angle = 270
elif key == arcade.key.RIGHT:
self.player.shoot_right_pressed = True
elif key == arcade.key.UP:
self.player.shoot_up_pressed = True
elif key == arcade.key.LEFT:
self.player.shoot_left_pressed = True
elif key == arcade.key.DOWN:
self.player.shoot_down_pressed = True
def on_key_release(self, key, modifiers):
if key == arcade.key.W:
self.player.change_y = 0
elif key == arcade.key.A:
self.player.change_x = 0
elif key == arcade.key.S:
self.player.change_y = 0
elif key == arcade.key.D:
self.player.change_x = 0
elif key == arcade.key.RIGHT:
self.player.shoot_right_pressed = False
elif key == arcade.key.UP:
self.player.shoot_up_pressed = False
elif key == arcade.key.LEFT:
self.player.shoot_left_pressed = False
elif key == arcade.key.DOWN:
self.player.shoot_down_pressed = False
def spawn_bullet(self, angle_in_deg):
# only allow bullet to spawn on an interval
if self.bullet_cooldown < BULLET_COOLDOWN_TICKS:
return
self.bullet_cooldown = 0
bullet = arcade.Sprite("images/laserBlue01.png", 0.75)
# Position the bullet at the player's current location
start_x = self.player.center_x
start_y = self.player.center_y
bullet.center_x = start_x
bullet.center_y = start_y
# angle the bullet visually
bullet.angle = angle_in_deg
angle_in_rad = math.radians(angle_in_deg)
# set bullet's movement direction
bullet.change_x = math.cos(angle_in_rad) * BULLET_SPEED
bullet.change_y = math.sin(angle_in_rad) * BULLET_SPEED
# Add the bullet to the appropriate lists
self.bullet_list.append(bullet)
def on_draw(self):
# clear screen and start render process
arcade.start_render()
# draw game items
self.bullet_list.draw()
self.enemy_list.draw()
self.player.draw()
# Put the score on the screen.
output = f"Score: {self.score}"
arcade.draw_text(output, 10, 20, arcade.color.WHITE, 14)
# Game over message
if self.game_over:
arcade.draw_text("Game Over", SCREEN_WIDTH/2, SCREEN_HEIGHT/2, arcade.color.WHITE, 100, width=SCREEN_WIDTH,
align="center", anchor_x="center", anchor_y="center")
if __name__ == "__main__":
game = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
arcade.run()

View File

@ -1,777 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.0" tiledversion="1.1.5" orientation="isometric" renderorder="right-down" width="10" height="10" tilewidth="256" tileheight="149" infinite="0" nextobjectid="1">
<tileset firstgid="1" name="Dungeon" tilewidth="256" tileheight="512" tilecount="252" columns="0">
<grid orientation="orthogonal" width="1" height="1"/>
<tile id="0">
<image width="256" height="512" source="isometric_dungeon/barrel_E.png"/>
</tile>
<tile id="1">
<image width="256" height="512" source="isometric_dungeon/barrel_N.png"/>
</tile>
<tile id="2">
<image width="256" height="512" source="isometric_dungeon/barrel_S.png"/>
</tile>
<tile id="3">
<image width="256" height="512" source="isometric_dungeon/barrel_W.png"/>
</tile>
<tile id="4">
<image width="256" height="512" source="isometric_dungeon/barrels_E.png"/>
</tile>
<tile id="5">
<image width="256" height="512" source="isometric_dungeon/barrels_N.png"/>
</tile>
<tile id="6">
<image width="256" height="512" source="isometric_dungeon/barrels_S.png"/>
</tile>
<tile id="7">
<image width="256" height="512" source="isometric_dungeon/barrels_W.png"/>
</tile>
<tile id="8">
<image width="256" height="512" source="isometric_dungeon/barrelsStacked_E.png"/>
</tile>
<tile id="9">
<image width="256" height="512" source="isometric_dungeon/barrelsStacked_N.png"/>
</tile>
<tile id="10">
<image width="256" height="512" source="isometric_dungeon/barrelsStacked_S.png"/>
</tile>
<tile id="11">
<image width="256" height="512" source="isometric_dungeon/barrelsStacked_W.png"/>
</tile>
<tile id="12">
<image width="256" height="512" source="isometric_dungeon/bridge_E.png"/>
</tile>
<tile id="13">
<image width="256" height="512" source="isometric_dungeon/bridge_N.png"/>
</tile>
<tile id="14">
<image width="256" height="512" source="isometric_dungeon/bridge_S.png"/>
</tile>
<tile id="15">
<image width="256" height="512" source="isometric_dungeon/bridge_W.png"/>
</tile>
<tile id="16">
<image width="256" height="512" source="isometric_dungeon/bridgeBroken_E.png"/>
</tile>
<tile id="17">
<image width="256" height="512" source="isometric_dungeon/bridgeBroken_N.png"/>
</tile>
<tile id="18">
<image width="256" height="512" source="isometric_dungeon/bridgeBroken_S.png"/>
</tile>
<tile id="19">
<image width="256" height="512" source="isometric_dungeon/bridgeBroken_W.png"/>
</tile>
<tile id="20">
<image width="256" height="512" source="isometric_dungeon/chair_E.png"/>
</tile>
<tile id="21">
<image width="256" height="512" source="isometric_dungeon/chair_N.png"/>
</tile>
<tile id="22">
<image width="256" height="512" source="isometric_dungeon/chair_S.png"/>
</tile>
<tile id="23">
<image width="256" height="512" source="isometric_dungeon/chair_W.png"/>
</tile>
<tile id="24">
<image width="256" height="512" source="isometric_dungeon/chestClosed_E.png"/>
</tile>
<tile id="25">
<image width="256" height="512" source="isometric_dungeon/chestClosed_N.png"/>
</tile>
<tile id="26">
<image width="256" height="512" source="isometric_dungeon/chestClosed_S.png"/>
</tile>
<tile id="27">
<image width="256" height="512" source="isometric_dungeon/chestClosed_W.png"/>
</tile>
<tile id="28">
<image width="256" height="512" source="isometric_dungeon/chestOpen_E.png"/>
</tile>
<tile id="29">
<image width="256" height="512" source="isometric_dungeon/chestOpen_N.png"/>
</tile>
<tile id="30">
<image width="256" height="512" source="isometric_dungeon/chestOpen_S.png"/>
</tile>
<tile id="31">
<image width="256" height="512" source="isometric_dungeon/chestOpen_W.png"/>
</tile>
<tile id="32">
<image width="256" height="512" source="isometric_dungeon/dirt_E.png"/>
</tile>
<tile id="33">
<image width="256" height="512" source="isometric_dungeon/dirt_N.png"/>
</tile>
<tile id="34">
<image width="256" height="512" source="isometric_dungeon/dirt_S.png"/>
</tile>
<tile id="35">
<image width="256" height="512" source="isometric_dungeon/dirt_W.png"/>
</tile>
<tile id="36">
<image width="256" height="512" source="isometric_dungeon/dirtTiles_E.png"/>
</tile>
<tile id="37">
<image width="256" height="512" source="isometric_dungeon/dirtTiles_N.png"/>
</tile>
<tile id="38">
<image width="256" height="512" source="isometric_dungeon/dirtTiles_S.png"/>
</tile>
<tile id="39">
<image width="256" height="512" source="isometric_dungeon/dirtTiles_W.png"/>
</tile>
<tile id="40">
<image width="256" height="512" source="isometric_dungeon/planks_E.png"/>
</tile>
<tile id="41">
<image width="256" height="512" source="isometric_dungeon/planks_N.png"/>
</tile>
<tile id="42">
<image width="256" height="512" source="isometric_dungeon/planks_S.png"/>
</tile>
<tile id="43">
<image width="256" height="512" source="isometric_dungeon/planks_W.png"/>
</tile>
<tile id="44">
<image width="256" height="512" source="isometric_dungeon/planksBroken_E.png"/>
</tile>
<tile id="45">
<image width="256" height="512" source="isometric_dungeon/planksBroken_N.png"/>
</tile>
<tile id="46">
<image width="256" height="512" source="isometric_dungeon/planksBroken_S.png"/>
</tile>
<tile id="47">
<image width="256" height="512" source="isometric_dungeon/planksBroken_W.png"/>
</tile>
<tile id="48">
<image width="256" height="512" source="isometric_dungeon/planksHole_E.png"/>
</tile>
<tile id="49">
<image width="256" height="512" source="isometric_dungeon/planksHole_N.png"/>
</tile>
<tile id="50">
<image width="256" height="512" source="isometric_dungeon/planksHole_S.png"/>
</tile>
<tile id="51">
<image width="256" height="512" source="isometric_dungeon/planksHole_W.png"/>
</tile>
<tile id="52">
<image width="256" height="512" source="isometric_dungeon/stairs_E.png"/>
</tile>
<tile id="53">
<image width="256" height="512" source="isometric_dungeon/stairs_N.png"/>
</tile>
<tile id="54">
<image width="256" height="512" source="isometric_dungeon/stairs_S.png"/>
</tile>
<tile id="55">
<image width="256" height="512" source="isometric_dungeon/stairs_W.png"/>
</tile>
<tile id="56">
<image width="256" height="512" source="isometric_dungeon/stairsAged_E.png"/>
</tile>
<tile id="57">
<image width="256" height="512" source="isometric_dungeon/stairsAged_N.png"/>
</tile>
<tile id="58">
<image width="256" height="512" source="isometric_dungeon/stairsAged_S.png"/>
</tile>
<tile id="59">
<image width="256" height="512" source="isometric_dungeon/stairsAged_W.png"/>
</tile>
<tile id="60">
<image width="256" height="512" source="isometric_dungeon/stairsSpiral_E.png"/>
</tile>
<tile id="61">
<image width="256" height="512" source="isometric_dungeon/stairsSpiral_N.png"/>
</tile>
<tile id="62">
<image width="256" height="512" source="isometric_dungeon/stairsSpiral_S.png"/>
</tile>
<tile id="63">
<image width="256" height="512" source="isometric_dungeon/stairsSpiral_W.png"/>
</tile>
<tile id="64">
<image width="256" height="512" source="isometric_dungeon/stone_E.png"/>
</tile>
<tile id="65">
<image width="256" height="512" source="isometric_dungeon/stone_N.png"/>
</tile>
<tile id="66">
<image width="256" height="512" source="isometric_dungeon/stone_S.png"/>
</tile>
<tile id="67">
<image width="256" height="512" source="isometric_dungeon/stone_W.png"/>
</tile>
<tile id="68">
<image width="256" height="512" source="isometric_dungeon/stoneColumn_E.png"/>
</tile>
<tile id="69">
<image width="256" height="512" source="isometric_dungeon/stoneColumn_N.png"/>
</tile>
<tile id="70">
<image width="256" height="512" source="isometric_dungeon/stoneColumn_S.png"/>
</tile>
<tile id="71">
<image width="256" height="512" source="isometric_dungeon/stoneColumn_W.png"/>
</tile>
<tile id="72">
<image width="256" height="512" source="isometric_dungeon/stoneColumnWood_E.png"/>
</tile>
<tile id="73">
<image width="256" height="512" source="isometric_dungeon/stoneColumnWood_N.png"/>
</tile>
<tile id="74">
<image width="256" height="512" source="isometric_dungeon/stoneColumnWood_S.png"/>
</tile>
<tile id="75">
<image width="256" height="512" source="isometric_dungeon/stoneColumnWood_W.png"/>
</tile>
<tile id="76">
<image width="256" height="512" source="isometric_dungeon/stoneLeft_E.png"/>
</tile>
<tile id="77">
<image width="256" height="512" source="isometric_dungeon/stoneLeft_N.png"/>
</tile>
<tile id="78">
<image width="256" height="512" source="isometric_dungeon/stoneLeft_S.png"/>
</tile>
<tile id="79">
<image width="256" height="512" source="isometric_dungeon/stoneLeft_W.png"/>
</tile>
<tile id="80">
<image width="256" height="512" source="isometric_dungeon/stoneMissingTiles_E.png"/>
</tile>
<tile id="81">
<image width="256" height="512" source="isometric_dungeon/stoneMissingTiles_N.png"/>
</tile>
<tile id="82">
<image width="256" height="512" source="isometric_dungeon/stoneMissingTiles_S.png"/>
</tile>
<tile id="83">
<image width="256" height="512" source="isometric_dungeon/stoneMissingTiles_W.png"/>
</tile>
<tile id="84">
<image width="256" height="512" source="isometric_dungeon/stoneRight_E.png"/>
</tile>
<tile id="85">
<image width="256" height="512" source="isometric_dungeon/stoneRight_N.png"/>
</tile>
<tile id="86">
<image width="256" height="512" source="isometric_dungeon/stoneRight_S.png"/>
</tile>
<tile id="87">
<image width="256" height="512" source="isometric_dungeon/stoneRight_W.png"/>
</tile>
<tile id="88">
<image width="256" height="512" source="isometric_dungeon/stoneSide_E.png"/>
</tile>
<tile id="89">
<image width="256" height="512" source="isometric_dungeon/stoneSide_N.png"/>
</tile>
<tile id="90">
<image width="256" height="512" source="isometric_dungeon/stoneSide_S.png"/>
</tile>
<tile id="91">
<image width="256" height="512" source="isometric_dungeon/stoneSide_W.png"/>
</tile>
<tile id="92">
<image width="256" height="512" source="isometric_dungeon/stoneSideUneven_E.png"/>
</tile>
<tile id="93">
<image width="256" height="512" source="isometric_dungeon/stoneSideUneven_N.png"/>
</tile>
<tile id="94">
<image width="256" height="512" source="isometric_dungeon/stoneSideUneven_S.png"/>
</tile>
<tile id="95">
<image width="256" height="512" source="isometric_dungeon/stoneSideUneven_W.png"/>
</tile>
<tile id="96">
<image width="256" height="512" source="isometric_dungeon/stoneStep_E.png"/>
</tile>
<tile id="97">
<image width="256" height="512" source="isometric_dungeon/stoneStep_N.png"/>
</tile>
<tile id="98">
<image width="256" height="512" source="isometric_dungeon/stoneStep_S.png"/>
</tile>
<tile id="99">
<image width="256" height="512" source="isometric_dungeon/stoneStep_W.png"/>
</tile>
<tile id="100">
<image width="256" height="512" source="isometric_dungeon/stoneSteps_E.png"/>
</tile>
<tile id="101">
<image width="256" height="512" source="isometric_dungeon/stoneSteps_N.png"/>
</tile>
<tile id="102">
<image width="256" height="512" source="isometric_dungeon/stoneSteps_S.png"/>
</tile>
<tile id="103">
<image width="256" height="512" source="isometric_dungeon/stoneSteps_W.png"/>
</tile>
<tile id="104">
<image width="256" height="512" source="isometric_dungeon/stoneTile_E.png"/>
</tile>
<tile id="105">
<image width="256" height="512" source="isometric_dungeon/stoneTile_N.png"/>
</tile>
<tile id="106">
<image width="256" height="512" source="isometric_dungeon/stoneTile_S.png"/>
</tile>
<tile id="107">
<image width="256" height="512" source="isometric_dungeon/stoneTile_W.png"/>
</tile>
<tile id="108">
<image width="256" height="512" source="isometric_dungeon/stoneUneven_E.png"/>
</tile>
<tile id="109">
<image width="256" height="512" source="isometric_dungeon/stoneUneven_N.png"/>
</tile>
<tile id="110">
<image width="256" height="512" source="isometric_dungeon/stoneUneven_S.png"/>
</tile>
<tile id="111">
<image width="256" height="512" source="isometric_dungeon/stoneUneven_W.png"/>
</tile>
<tile id="112">
<image width="256" height="512" source="isometric_dungeon/stoneWall_E.png"/>
</tile>
<tile id="113">
<image width="256" height="512" source="isometric_dungeon/stoneWall_N.png"/>
</tile>
<tile id="114">
<image width="256" height="512" source="isometric_dungeon/stoneWall_S.png"/>
</tile>
<tile id="115">
<image width="256" height="512" source="isometric_dungeon/stoneWall_W.png"/>
</tile>
<tile id="116">
<image width="256" height="512" source="isometric_dungeon/stoneWallAged_E.png"/>
</tile>
<tile id="117">
<image width="256" height="512" source="isometric_dungeon/stoneWallAged_N.png"/>
</tile>
<tile id="118">
<image width="256" height="512" source="isometric_dungeon/stoneWallAged_S.png"/>
</tile>
<tile id="119">
<image width="256" height="512" source="isometric_dungeon/stoneWallAged_W.png"/>
</tile>
<tile id="120">
<image width="256" height="512" source="isometric_dungeon/stoneWallAgedLeft_E.png"/>
</tile>
<tile id="121">
<image width="256" height="512" source="isometric_dungeon/stoneWallAgedLeft_N.png"/>
</tile>
<tile id="122">
<image width="256" height="512" source="isometric_dungeon/stoneWallAgedLeft_S.png"/>
</tile>
<tile id="123">
<image width="256" height="512" source="isometric_dungeon/stoneWallAgedLeft_W.png"/>
</tile>
<tile id="124">
<image width="256" height="512" source="isometric_dungeon/stoneWallAgedRight_E.png"/>
</tile>
<tile id="125">
<image width="256" height="512" source="isometric_dungeon/stoneWallAgedRight_N.png"/>
</tile>
<tile id="126">
<image width="256" height="512" source="isometric_dungeon/stoneWallAgedRight_S.png"/>
</tile>
<tile id="127">
<image width="256" height="512" source="isometric_dungeon/stoneWallAgedRight_W.png"/>
</tile>
<tile id="128">
<image width="256" height="512" source="isometric_dungeon/stoneWallArchway_E.png"/>
</tile>
<tile id="129">
<image width="256" height="512" source="isometric_dungeon/stoneWallArchway_N.png"/>
</tile>
<tile id="130">
<image width="256" height="512" source="isometric_dungeon/stoneWallArchway_S.png"/>
</tile>
<tile id="131">
<image width="256" height="512" source="isometric_dungeon/stoneWallArchway_W.png"/>
</tile>
<tile id="132">
<image width="256" height="512" source="isometric_dungeon/stoneWallBroken_E.png"/>
</tile>
<tile id="133">
<image width="256" height="512" source="isometric_dungeon/stoneWallBroken_N.png"/>
</tile>
<tile id="134">
<image width="256" height="512" source="isometric_dungeon/stoneWallBroken_S.png"/>
</tile>
<tile id="135">
<image width="256" height="512" source="isometric_dungeon/stoneWallBroken_W.png"/>
</tile>
<tile id="136">
<image width="256" height="512" source="isometric_dungeon/stoneWallBrokenLeft_E.png"/>
</tile>
<tile id="137">
<image width="256" height="512" source="isometric_dungeon/stoneWallBrokenLeft_N.png"/>
</tile>
<tile id="138">
<image width="256" height="512" source="isometric_dungeon/stoneWallBrokenLeft_S.png"/>
</tile>
<tile id="139">
<image width="256" height="512" source="isometric_dungeon/stoneWallBrokenLeft_W.png"/>
</tile>
<tile id="140">
<image width="256" height="512" source="isometric_dungeon/stoneWallBrokenRight_E.png"/>
</tile>
<tile id="141">
<image width="256" height="512" source="isometric_dungeon/stoneWallBrokenRight_N.png"/>
</tile>
<tile id="142">
<image width="256" height="512" source="isometric_dungeon/stoneWallBrokenRight_S.png"/>
</tile>
<tile id="143">
<image width="256" height="512" source="isometric_dungeon/stoneWallBrokenRight_W.png"/>
</tile>
<tile id="144">
<image width="256" height="512" source="isometric_dungeon/stoneWallColumn_E.png"/>
</tile>
<tile id="145">
<image width="256" height="512" source="isometric_dungeon/stoneWallColumn_N.png"/>
</tile>
<tile id="146">
<image width="256" height="512" source="isometric_dungeon/stoneWallColumn_S.png"/>
</tile>
<tile id="147">
<image width="256" height="512" source="isometric_dungeon/stoneWallColumn_W.png"/>
</tile>
<tile id="148">
<image width="256" height="512" source="isometric_dungeon/stoneWallColumnIn_E.png"/>
</tile>
<tile id="149">
<image width="256" height="512" source="isometric_dungeon/stoneWallColumnIn_N.png"/>
</tile>
<tile id="150">
<image width="256" height="512" source="isometric_dungeon/stoneWallColumnIn_S.png"/>
</tile>
<tile id="151">
<image width="256" height="512" source="isometric_dungeon/stoneWallColumnIn_W.png"/>
</tile>
<tile id="152">
<image width="256" height="512" source="isometric_dungeon/stoneWallCorner_E.png"/>
</tile>
<tile id="153">
<image width="256" height="512" source="isometric_dungeon/stoneWallCorner_N.png"/>
</tile>
<tile id="154">
<image width="256" height="512" source="isometric_dungeon/stoneWallCorner_S.png"/>
</tile>
<tile id="155">
<image width="256" height="512" source="isometric_dungeon/stoneWallCorner_W.png"/>
</tile>
<tile id="156">
<image width="256" height="512" source="isometric_dungeon/stoneWallDoor_E.png"/>
</tile>
<tile id="157">
<image width="256" height="512" source="isometric_dungeon/stoneWallDoor_N.png"/>
</tile>
<tile id="158">
<image width="256" height="512" source="isometric_dungeon/stoneWallDoor_S.png"/>
</tile>
<tile id="159">
<image width="256" height="512" source="isometric_dungeon/stoneWallDoor_W.png"/>
</tile>
<tile id="160">
<image width="256" height="512" source="isometric_dungeon/stoneWallDoorBars_E.png"/>
</tile>
<tile id="161">
<image width="256" height="512" source="isometric_dungeon/stoneWallDoorBars_N.png"/>
</tile>
<tile id="162">
<image width="256" height="512" source="isometric_dungeon/stoneWallDoorBars_S.png"/>
</tile>
<tile id="163">
<image width="256" height="512" source="isometric_dungeon/stoneWallDoorBars_W.png"/>
</tile>
<tile id="164">
<image width="256" height="512" source="isometric_dungeon/stoneWallDoorClosed_E.png"/>
</tile>
<tile id="165">
<image width="256" height="512" source="isometric_dungeon/stoneWallDoorClosed_N.png"/>
</tile>
<tile id="166">
<image width="256" height="512" source="isometric_dungeon/stoneWallDoorClosed_S.png"/>
</tile>
<tile id="167">
<image width="256" height="512" source="isometric_dungeon/stoneWallDoorClosed_W.png"/>
</tile>
<tile id="168">
<image width="256" height="512" source="isometric_dungeon/stoneWallDoorOpen_E.png"/>
</tile>
<tile id="169">
<image width="256" height="512" source="isometric_dungeon/stoneWallDoorOpen_N.png"/>
</tile>
<tile id="170">
<image width="256" height="512" source="isometric_dungeon/stoneWallDoorOpen_S.png"/>
</tile>
<tile id="171">
<image width="256" height="512" source="isometric_dungeon/stoneWallDoorOpen_W.png"/>
</tile>
<tile id="172">
<image width="256" height="512" source="isometric_dungeon/stoneWallGate_E.png"/>
</tile>
<tile id="173">
<image width="256" height="512" source="isometric_dungeon/stoneWallGate_N.png"/>
</tile>
<tile id="174">
<image width="256" height="512" source="isometric_dungeon/stoneWallGate_S.png"/>
</tile>
<tile id="175">
<image width="256" height="512" source="isometric_dungeon/stoneWallGate_W.png"/>
</tile>
<tile id="176">
<image width="256" height="512" source="isometric_dungeon/stoneWallGateBars_E.png"/>
</tile>
<tile id="177">
<image width="256" height="512" source="isometric_dungeon/stoneWallGateBars_N.png"/>
</tile>
<tile id="178">
<image width="256" height="512" source="isometric_dungeon/stoneWallGateBars_S.png"/>
</tile>
<tile id="179">
<image width="256" height="512" source="isometric_dungeon/stoneWallGateBars_W.png"/>
</tile>
<tile id="180">
<image width="256" height="512" source="isometric_dungeon/stoneWallGateClosed_E.png"/>
</tile>
<tile id="181">
<image width="256" height="512" source="isometric_dungeon/stoneWallGateClosed_N.png"/>
</tile>
<tile id="182">
<image width="256" height="512" source="isometric_dungeon/stoneWallGateClosed_S.png"/>
</tile>
<tile id="183">
<image width="256" height="512" source="isometric_dungeon/stoneWallGateClosed_W.png"/>
</tile>
<tile id="184">
<image width="256" height="512" source="isometric_dungeon/stoneWallGateOpen_E.png"/>
</tile>
<tile id="185">
<image width="256" height="512" source="isometric_dungeon/stoneWallGateOpen_N.png"/>
</tile>
<tile id="186">
<image width="256" height="512" source="isometric_dungeon/stoneWallGateOpen_S.png"/>
</tile>
<tile id="187">
<image width="256" height="512" source="isometric_dungeon/stoneWallGateOpen_W.png"/>
</tile>
<tile id="188">
<image width="256" height="512" source="isometric_dungeon/stoneWallHole_E.png"/>
</tile>
<tile id="189">
<image width="256" height="512" source="isometric_dungeon/stoneWallHole_N.png"/>
</tile>
<tile id="190">
<image width="256" height="512" source="isometric_dungeon/stoneWallHole_S.png"/>
</tile>
<tile id="191">
<image width="256" height="512" source="isometric_dungeon/stoneWallHole_W.png"/>
</tile>
<tile id="192">
<image width="256" height="512" source="isometric_dungeon/stoneWallTop_E.png"/>
</tile>
<tile id="193">
<image width="256" height="512" source="isometric_dungeon/stoneWallTop_N.png"/>
</tile>
<tile id="194">
<image width="256" height="512" source="isometric_dungeon/stoneWallTop_S.png"/>
</tile>
<tile id="195">
<image width="256" height="512" source="isometric_dungeon/stoneWallTop_W.png"/>
</tile>
<tile id="196">
<image width="256" height="512" source="isometric_dungeon/stoneWallWindow_E.png"/>
</tile>
<tile id="197">
<image width="256" height="512" source="isometric_dungeon/stoneWallWindow_N.png"/>
</tile>
<tile id="198">
<image width="256" height="512" source="isometric_dungeon/stoneWallWindow_S.png"/>
</tile>
<tile id="199">
<image width="256" height="512" source="isometric_dungeon/stoneWallWindow_W.png"/>
</tile>
<tile id="200">
<image width="256" height="512" source="isometric_dungeon/stoneWallWindowBars_E.png"/>
</tile>
<tile id="201">
<image width="256" height="512" source="isometric_dungeon/stoneWallWindowBars_N.png"/>
</tile>
<tile id="202">
<image width="256" height="512" source="isometric_dungeon/stoneWallWindowBars_S.png"/>
</tile>
<tile id="203">
<image width="256" height="512" source="isometric_dungeon/stoneWallWindowBars_W.png"/>
</tile>
<tile id="204">
<image width="256" height="512" source="isometric_dungeon/tableChairsBroken_E.png"/>
</tile>
<tile id="205">
<image width="256" height="512" source="isometric_dungeon/tableChairsBroken_N.png"/>
</tile>
<tile id="206">
<image width="256" height="512" source="isometric_dungeon/tableChairsBroken_S.png"/>
</tile>
<tile id="207">
<image width="256" height="512" source="isometric_dungeon/tableChairsBroken_W.png"/>
</tile>
<tile id="208">
<image width="256" height="512" source="isometric_dungeon/tableRound_E.png"/>
</tile>
<tile id="209">
<image width="256" height="512" source="isometric_dungeon/tableRound_N.png"/>
</tile>
<tile id="210">
<image width="256" height="512" source="isometric_dungeon/tableRound_S.png"/>
</tile>
<tile id="211">
<image width="256" height="512" source="isometric_dungeon/tableRound_W.png"/>
</tile>
<tile id="212">
<image width="256" height="512" source="isometric_dungeon/tableRoundChairs_E.png"/>
</tile>
<tile id="213">
<image width="256" height="512" source="isometric_dungeon/tableRoundChairs_N.png"/>
</tile>
<tile id="214">
<image width="256" height="512" source="isometric_dungeon/tableRoundChairs_S.png"/>
</tile>
<tile id="215">
<image width="256" height="512" source="isometric_dungeon/tableRoundChairs_W.png"/>
</tile>
<tile id="216">
<image width="256" height="512" source="isometric_dungeon/tableShort_E.png"/>
</tile>
<tile id="217">
<image width="256" height="512" source="isometric_dungeon/tableShort_N.png"/>
</tile>
<tile id="218">
<image width="256" height="512" source="isometric_dungeon/tableShort_S.png"/>
</tile>
<tile id="219">
<image width="256" height="512" source="isometric_dungeon/tableShort_W.png"/>
</tile>
<tile id="220">
<image width="256" height="512" source="isometric_dungeon/tableShortChairs_E.png"/>
</tile>
<tile id="221">
<image width="256" height="512" source="isometric_dungeon/tableShortChairs_N.png"/>
</tile>
<tile id="222">
<image width="256" height="512" source="isometric_dungeon/tableShortChairs_S.png"/>
</tile>
<tile id="223">
<image width="256" height="512" source="isometric_dungeon/tableShortChairs_W.png"/>
</tile>
<tile id="224">
<image width="256" height="512" source="isometric_dungeon/woodenCrate_E.png"/>
</tile>
<tile id="225">
<image width="256" height="512" source="isometric_dungeon/woodenCrate_N.png"/>
</tile>
<tile id="226">
<image width="256" height="512" source="isometric_dungeon/woodenCrate_S.png"/>
</tile>
<tile id="227">
<image width="256" height="512" source="isometric_dungeon/woodenCrate_W.png"/>
</tile>
<tile id="228">
<image width="256" height="512" source="isometric_dungeon/woodenCrates_E.png"/>
</tile>
<tile id="229">
<image width="256" height="512" source="isometric_dungeon/woodenCrates_N.png"/>
</tile>
<tile id="230">
<image width="256" height="512" source="isometric_dungeon/woodenCrates_S.png"/>
</tile>
<tile id="231">
<image width="256" height="512" source="isometric_dungeon/woodenCrates_W.png"/>
</tile>
<tile id="232">
<image width="256" height="512" source="isometric_dungeon/woodenPile_E.png"/>
</tile>
<tile id="233">
<image width="256" height="512" source="isometric_dungeon/woodenPile_N.png"/>
</tile>
<tile id="234">
<image width="256" height="512" source="isometric_dungeon/woodenPile_S.png"/>
</tile>
<tile id="235">
<image width="256" height="512" source="isometric_dungeon/woodenPile_W.png"/>
</tile>
<tile id="236">
<image width="256" height="512" source="isometric_dungeon/woodenSupportBeams_E.png"/>
</tile>
<tile id="237">
<image width="256" height="512" source="isometric_dungeon/woodenSupportBeams_N.png"/>
</tile>
<tile id="238">
<image width="256" height="512" source="isometric_dungeon/woodenSupportBeams_S.png"/>
</tile>
<tile id="239">
<image width="256" height="512" source="isometric_dungeon/woodenSupportBeams_W.png"/>
</tile>
<tile id="240">
<image width="256" height="512" source="isometric_dungeon/woodenSupports_E.png"/>
</tile>
<tile id="241">
<image width="256" height="512" source="isometric_dungeon/woodenSupports_N.png"/>
</tile>
<tile id="242">
<image width="256" height="512" source="isometric_dungeon/woodenSupports_S.png"/>
</tile>
<tile id="243">
<image width="256" height="512" source="isometric_dungeon/woodenSupports_W.png"/>
</tile>
<tile id="244">
<image width="256" height="512" source="isometric_dungeon/woodenSupportsBeam_E.png"/>
</tile>
<tile id="245">
<image width="256" height="512" source="isometric_dungeon/woodenSupportsBeam_N.png"/>
</tile>
<tile id="246">
<image width="256" height="512" source="isometric_dungeon/woodenSupportsBeam_S.png"/>
</tile>
<tile id="247">
<image width="256" height="512" source="isometric_dungeon/woodenSupportsBeam_W.png"/>
</tile>
<tile id="248">
<image width="256" height="512" source="isometric_dungeon/woodenSupportsBlock_E.png"/>
</tile>
<tile id="249">
<image width="256" height="512" source="isometric_dungeon/woodenSupportsBlock_N.png"/>
</tile>
<tile id="250">
<image width="256" height="512" source="isometric_dungeon/woodenSupportsBlock_S.png"/>
</tile>
<tile id="251">
<image width="256" height="512" source="isometric_dungeon/woodenSupportsBlock_W.png"/>
</tile>
</tileset>
<layer name="Floor" width="10" height="10">
<data encoding="base64" compression="zlib">
eJxtkEsOgCAMRLmN5+CzNuGzceMBVIT7ryzJa0KIi5d2YEpHgzHGCYl6CV3wYH9qEF5qE25mdN6iA7qiI9487fT4duHEVznzE453N+HgfujCO1obvWXG4Y30mtNPnkzOTL7hf7iLyzdqrsZMXPZ1+sY/KvgTWndrpoo/c/4BPasgKg==
</data>
</layer>
<layer name="Walls" width="10" height="10">
<data encoding="base64" compression="zlib">
eJybxcDAUATFM5HYRWjiJQwIsJMBOyiFqpuIxC9Hws1QejaaeaUkmIdPHQxspdC8nSS6jxh1c4C4GIpnI7GL0cQBoyMZ0g==
</data>
</layer>
<layer name="Furniture" width="10" height="10">
<data encoding="base64" compression="zlib">
eJx7wUB98IBC/d/R+GcpNG8oAQAw7AON
</data>
</layer>
</map>

View File

@ -1,58 +0,0 @@
"""
Helper class to track length of time taken for each frame and draw a graph when application exits.
Also able to add events at arbitrary times across the graph.
"""
import time
import matplotlib.pyplot as plt
import statistics
class FrametimePlotter:
EVENT_POINT_Y = -0.05
EVENT_MSG_Y = -0.045
def __init__(self):
self.times = []
self.events = []
self.start = time.perf_counter()
def add_event(self, event_msg):
self.events.append((len(self.times), event_msg))
def end_frame(self, time_delta):
self.times.append(time_delta)
def _show_stats(self):
end = time.perf_counter()
print("Min : {:.5f}".format(min(self.times)))
print("Max : {:.5f}".format(max(self.times)))
print("Avg : {:.5f}".format(statistics.mean(self.times)))
print("Median: {:.5f}".format(statistics.median(self.times)))
try:
print("Mode : {:.5f}".format(statistics.mode(self.times)))
except statistics.StatisticsError as e:
print("Mode : {}".format(e))
print("StdDev: {:.5f}".format(statistics.stdev(self.times)))
frame_count = len(self.times)
elapsed_time = end - self.start
print("Frame count: {}".format(frame_count))
print("Elapsed time: {:.5f}".format(elapsed_time))
print("FPS: {:.5f}".format(frame_count / elapsed_time))
def show(self):
if len(self.times) <= 1:
return
self._show_stats()
frame_idxs = range(0, len(self.times))
event_idxs = [e[0] for e in self.events]
event_point_y = [self.EVENT_POINT_Y] * len(self.events)
plt.figure("Frame durations", figsize=(8, 6))
plt.plot(frame_idxs, self.times, event_idxs, event_point_y, "k|")
plt.xlabel("frames")
plt.ylabel("frame duration")
plt.ylim(self.EVENT_POINT_Y - 0.005, 0.5)
plt.tight_layout()
for frame_idx, msg in self.events:
plt.text(frame_idx, self.EVENT_MSG_Y, msg, horizontalalignment="center", verticalalignment="bottom",
size="smaller", rotation="vertical")
plt.show()

View File

@ -1,108 +0,0 @@
"""
Use sprites to scroll around a large screen.
Simple program to show basic sprite usage.
Artwork from http://kenney.nl
If Python and Arcade are installed, this example can be run from the command line with:
python -m arcade.examples.full_screen_example
"""
import arcade
import os
SPRITE_SCALING = 0.5
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
SCREEN_TITLE = "Full Screen Example"
# How many pixels to keep as a minimum margin between the character
# and the edge of the screen.
VIEWPORT_MARGIN = 40
MOVEMENT_SPEED = 5
class MyGame(arcade.Window):
""" Main application class. """
def __init__(self):
"""
Initializer
"""
# Open a window in full screen mode. Remove fullscreen=True if
# you don't want to start this way.
super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE, fullscreen=True)
# Set the working directory (where we expect to find files) to the same
# directory this .py file is in. You can leave this out of your own
# code, but it is needed to easily run the examples using "python -m"
# as mentioned at the top of this program.
file_path = os.path.dirname(os.path.abspath(__file__))
os.chdir(file_path)
# This will get the size of the window, and set the viewport to match.
# So if the window is 1000x1000, then so will our viewport. If
# you want something different, then use those coordinates instead.
width, height = self.get_size()
self.set_viewport(0, width, 0, height)
arcade.set_background_color(arcade.color.AMAZON)
self.example_image = arcade.load_texture("images/boxCrate_double.png")
def on_draw(self):
"""
Render the screen.
"""
arcade.start_render()
# Get viewport dimensions
left, screen_width, bottom, screen_height = self.get_viewport()
text_size = 18
# Draw text on the screen so the user has an idea of what is happening
arcade.draw_text("Press F to toggle between full screen and windowed mode, unstretched.",
screen_width // 2, screen_height // 2 - 20,
arcade.color.WHITE, text_size, anchor_x="center")
arcade.draw_text("Press S to toggle between full screen and windowed mode, stretched.",
screen_width // 2, screen_height // 2 + 20,
arcade.color.WHITE, text_size, anchor_x="center")
# Draw some boxes on the bottom so we can see how they change
for x in range(64, 800, 128):
y = 64
width = 128
height = 128
arcade.draw_texture_rectangle(x, y, width, height, self.example_image)
def on_key_press(self, key, modifiers):
"""Called whenever a key is pressed. """
if key == arcade.key.F:
# User hits f. Flip between full and not full screen.
self.set_fullscreen(not self.fullscreen)
# Get the window coordinates. Match viewport to window coordinates
# so there is a one-to-one mapping.
width, height = self.get_size()
self.set_viewport(0, width, 0, height)
if key == arcade.key.S:
# User hits s. Flip between full and not full screen.
self.set_fullscreen(not self.fullscreen)
# Instead of a one-to-one mapping, stretch/squash window to match the
# constants. This does NOT respect aspect ratio. You'd need to
# do a bit of math for that.
self.set_viewport(0, SCREEN_WIDTH, 0, SCREEN_HEIGHT)
def main():
""" Main method """
MyGame()
arcade.run()
if __name__ == "__main__":
main()

View File

@ -1,92 +0,0 @@
"""
Drawing Gradients
If Python and Arcade are installed, this example can be run from the command line with:
python -m arcade.examples.gradients
"""
import arcade
# Do the math to figure out our screen dimensions
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
SCREEN_TITLE = "Gradients Example"
class MyGame(arcade.Window):
"""
Main application class.
"""
def __init__(self, width, height, title):
"""
Set up the application.
"""
super().__init__(width, height, title)
arcade.set_background_color(arcade.color.BLACK)
self.shapes = arcade.ShapeElementList()
# This is a large rectangle that fills the whole
# background. The gradient goes between the two colors
# top to bottom.
color1 = (215, 214, 165)
color2 = (219, 166, 123)
points = (0, 0), (SCREEN_WIDTH, 0), (SCREEN_WIDTH, SCREEN_HEIGHT), (0, SCREEN_HEIGHT)
colors = (color1, color1, color2, color2)
rect = arcade.create_rectangle_filled_with_colors(points, colors)
self.shapes.append(rect)
# Another rectangle, but in this case the color doesn't change. Just the
# transparency. This time it goes from left to right.
color1 = (165, 92, 85, 255)
color2 = (165, 92, 85, 0)
points = (100, 100), (SCREEN_WIDTH - 100, 100), (SCREEN_WIDTH - 100, 300), (100, 300)
colors = (color2, color1, color1, color2)
rect = arcade.create_rectangle_filled_with_colors(points, colors)
self.shapes.append(rect)
# Two lines
color1 = (7, 67, 88)
color2 = (69, 137, 133)
points = (100, 400), (SCREEN_WIDTH - 100, 400), (SCREEN_WIDTH - 100, 500), (100, 500)
colors = (color2, color1, color2, color1)
shape = arcade.create_lines_with_colors(points, colors, line_width=5)
self.shapes.append(shape)
# Triangle
color1 = (215, 214, 165)
color2 = (219, 166, 123)
color3 = (165, 92, 85)
points = (SCREEN_WIDTH // 2, 500), (SCREEN_WIDTH // 2 - 100, 400), (SCREEN_WIDTH // 2 + 100, 400)
colors = (color1, color2, color3)
shape = arcade.create_triangles_filled_with_colors(points, colors)
self.shapes.append(shape)
# Ellipse, gradient between center and outside
color1 = (69, 137, 133, 127)
color2 = (7, 67, 88, 127)
shape = arcade.create_ellipse_filled_with_colors(SCREEN_WIDTH // 2, 350, 50, 50,
inside_color=color1, outside_color=color2)
self.shapes.append(shape)
def on_draw(self):
"""
Render the screen.
"""
# This command has to happen before we start drawing
arcade.start_render()
self.shapes.draw()
# arcade.draw_rectangle_filled(500, 500, 50, 50, (255, 0, 0, 127))
def main():
MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
arcade.run()
if __name__ == "__main__":
main()

View File

@ -1,238 +0,0 @@
"""
Buttons with text on them
If Python and Arcade are installed, this example can be run from the command line with:
python -m arcade.examples.gui_text_button
"""
import arcade
import random
import os
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
SCREEN_TITLE = "GUI Text Buton Example"
class TextButton:
""" Text-based button """
def __init__(self,
center_x, center_y,
width, height,
text,
font_size=18,
font_face="Arial",
face_color=arcade.color.LIGHT_GRAY,
highlight_color=arcade.color.WHITE,
shadow_color=arcade.color.GRAY,
button_height=2):
self.center_x = center_x
self.center_y = center_y
self.width = width
self.height = height
self.text = text
self.font_size = font_size
self.font_face = font_face
self.pressed = False
self.face_color = face_color
self.highlight_color = highlight_color
self.shadow_color = shadow_color
self.button_height = button_height
def draw(self):
""" Draw the button """
arcade.draw_rectangle_filled(self.center_x, self.center_y, self.width,
self.height, self.face_color)
if not self.pressed:
color = self.shadow_color
else:
color = self.highlight_color
# Bottom horizontal
arcade.draw_line(self.center_x - self.width / 2, self.center_y - self.height / 2,
self.center_x + self.width / 2, self.center_y - self.height / 2,
color, self.button_height)
# Right vertical
arcade.draw_line(self.center_x + self.width / 2, self.center_y - self.height / 2,
self.center_x + self.width / 2, self.center_y + self.height / 2,
color, self.button_height)
if not self.pressed:
color = self.highlight_color
else:
color = self.shadow_color
# Top horizontal
arcade.draw_line(self.center_x - self.width / 2, self.center_y + self.height / 2,
self.center_x + self.width / 2, self.center_y + self.height / 2,
color, self.button_height)
# Left vertical
arcade.draw_line(self.center_x - self.width / 2, self.center_y - self.height / 2,
self.center_x - self.width / 2, self.center_y + self.height / 2,
color, self.button_height)
x = self.center_x
y = self.center_y
if not self.pressed:
x -= self.button_height
y += self.button_height
arcade.draw_text(self.text, x, y,
arcade.color.BLACK, font_size=self.font_size,
width=self.width, align="center",
anchor_x="center", anchor_y="center")
def on_press(self):
self.pressed = True
def on_release(self):
self.pressed = False
def check_mouse_press_for_buttons(x, y, button_list):
""" Given an x, y, see if we need to register any button clicks. """
for button in button_list:
if x > button.center_x + button.width / 2:
continue
if x < button.center_x - button.width / 2:
continue
if y > button.center_y + button.height / 2:
continue
if y < button.center_y - button.height / 2:
continue
button.on_press()
def check_mouse_release_for_buttons(x, y, button_list):
""" If a mouse button has been released, see if we need to process
any release events. """
for button in button_list:
if button.pressed:
button.on_release()
class StartTextButton(TextButton):
def __init__(self, center_x, center_y, action_function):
super().__init__(center_x, center_y, 100, 40, "Start", 18, "Arial")
self.action_function = action_function
def on_release(self):
super().on_release()
self.action_function()
class StopTextButton(TextButton):
def __init__(self, center_x, center_y, action_function):
super().__init__(center_x, center_y, 100, 40, "Stop", 18, "Arial")
self.action_function = action_function
def on_release(self):
super().on_release()
self.action_function()
class MyGame(arcade.Window):
"""
Main application class.
NOTE: Go ahead and delete the methods you don't need.
If you do need a method, delete the 'pass' and replace it
with your own code. Don't leave 'pass' in this program.
"""
def __init__(self, width, height, title):
super().__init__(width, height, title)
# Set the working directory (where we expect to find files) to the same
# directory this .py file is in. You can leave this out of your own
# code, but it is needed to easily run the examples using "python -m"
# as mentioned at the top of this program.
file_path = os.path.dirname(os.path.abspath(__file__))
os.chdir(file_path)
arcade.set_background_color(arcade.color.AMAZON)
self.pause = False
self.coin_list = None
self.button_list = None
def setup(self):
# Create your sprites and sprite lists here
self.coin_list = arcade.SpriteList()
for i in range(10):
coin = arcade.Sprite("images/coin_01.png", 0.25)
coin.center_x = random.randrange(SCREEN_WIDTH)
coin.center_y = random.randrange(SCREEN_HEIGHT)
coin.change_y = -1
self.coin_list.append(coin)
# Create our on-screen GUI buttons
self.button_list = []
play_button = StartTextButton(60, 570, self.resume_program)
self.button_list.append(play_button)
quit_button = StopTextButton(60, 515, self.pause_program)
self.button_list.append(quit_button)
def on_draw(self):
"""
Render the screen.
"""
arcade.start_render()
# Draw the coins
self.coin_list.draw()
# Draw the buttons
for button in self.button_list:
button.draw()
def update(self, delta_time):
"""
All the logic to move, and the game logic goes here.
Normally, you'll call update() on the sprite lists that
need it.
"""
if self.pause:
return
self.coin_list.update()
for coin in self.coin_list:
if coin.top < 0:
coin.bottom = SCREEN_HEIGHT
def on_mouse_press(self, x, y, button, key_modifiers):
"""
Called when the user presses a mouse button.
"""
check_mouse_press_for_buttons(x, y, self.button_list)
def on_mouse_release(self, x, y, button, key_modifiers):
"""
Called when a user releases a mouse button.
"""
check_mouse_release_for_buttons(x, y, self.button_list)
def pause_program(self):
self.pause = True
def resume_program(self):
self.pause = False
def main():
""" Main method """
game = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
game.setup()
arcade.run()
if __name__ == "__main__":
main()

View File

@ -1,48 +0,0 @@
"""
Drawing an example happy face
If Python and Arcade are installed, this example can be run from the command line with:
python -m arcade.examples.happy_face
"""
import arcade
# Set constants for the screen size
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 600
SCREEN_TITLE = "Happy Face Example"
# Open the window. Set the window title and dimensions
arcade.open_window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
# Set the background color
arcade.set_background_color(arcade.color.WHITE)
# Clear screen and start render process
arcade.start_render()
# --- Drawing Commands Will Go Here ---
# Draw the face
x = 300; y = 300; radius = 200
arcade.draw_circle_filled(x, y, radius, arcade.color.YELLOW)
# Draw the right eye
x = 370; y = 350; radius = 20
arcade.draw_circle_filled(x, y, radius, arcade.color.BLACK)
# Draw the left eye
x = 230; y = 350; radius = 20
arcade.draw_circle_filled(x, y, radius, arcade.color.BLACK)
# Draw the smile
x = 300; y = 280; width = 120; height = 100
start_angle = 190; end_angle = 350
arcade.draw_arc_outline(x, y, width, height, arcade.color.BLACK,
start_angle, end_angle, 10)
# Finish drawing and display the result
arcade.finish_render()
# Keep the window open until the user hits the 'close' button
arcade.run()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -1,100 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="95.954338"
height="95.954338"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="bumper.svg"
inkscape:export-filename="C:\Users\craven\Desktop\WebServer\arcade\examples\images\bumper.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="3.959798"
inkscape:cx="-18.856048"
inkscape:cy="41.144234"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1422"
inkscape:window-height="895"
inkscape:window-x="26"
inkscape:window-y="26"
inkscape:window-maximized="0"
showguides="true"
inkscape:guide-bbox="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-62.129459,-72.014217)">
<path
sodipodi:type="arc"
style="fill:#ff0000;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path2985"
sodipodi:cx="110.10663"
sodipodi:cy="119.99139"
sodipodi:rx="47.477169"
sodipodi:ry="47.477169"
d="m 157.5838,119.99139 c 0,26.22091 -21.25626,47.47717 -47.47717,47.47717 -26.220918,0 -47.477171,-21.25626 -47.477171,-47.47717 0,-26.22092 21.256253,-47.477173 47.477171,-47.477173 26.22091,0 47.47717,21.256253 47.47717,47.477173 z" />
<path
sodipodi:type="star"
style="fill:#ffff00;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path2991"
sodipodi:sides="10"
sodipodi:cx="78.286819"
sodipodi:cy="98.525642"
sodipodi:r1="50.346378"
sodipodi:r2="25.173187"
sodipodi:arg1="0.70372968"
sodipodi:arg2="1.0178889"
inkscape:flatsided="false"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 116.67262,131.10306 -25.165757,-11.155 -1.313808,27.49588 -13.802775,-23.81665 -17.224565,21.47239 2.832396,-27.38114 -26.556123,7.24719 18.385687,-20.48697 -25.744146,-9.746198 26.916272,-5.76747 -15.09878,-23.01687 25.165755,11.155007 1.313808,-27.495879 13.802775,23.816649 17.224564,-21.472396 -2.832395,27.381141 26.556122,-7.247188 -18.38569,20.486968 25.74415,9.746196 -26.91627,5.76747 z"
transform="matrix(0.90275471,0,0,0.91785308,38.801484,29.938127)" />
<path
sodipodi:type="arc"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path2995"
sodipodi:cx="110.35917"
sodipodi:cy="121.75915"
sodipodi:rx="22.475895"
sodipodi:ry="22.475895"
d="m 132.83506,121.75915 c 0,12.41309 -10.0628,22.47589 -22.47589,22.47589 -12.413095,0 -22.475896,-10.0628 -22.475896,-22.47589 0,-12.4131 10.062801,-22.475897 22.475896,-22.475897 12.41309,0 22.47589,10.062797 22.47589,22.475897 z"
transform="translate(-0.75761441,-1.767767)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Some files were not shown because too many files have changed in this diff Show More