optimize speed by rendering text by parts
@ -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
|
@ -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
|
@ -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]]
|
@ -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 = []
|
@ -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)
|
@ -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)
|
@ -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
|
@ -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
|
||||
)
|
||||
)
|
@ -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()
|
@ -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()
|
@ -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()
|
@ -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()
|
@ -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()
|
@ -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()
|
@ -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()
|
||||
|
@ -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()
|
@ -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()
|
@ -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()
|
@ -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()
|
@ -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()
|
@ -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>
|
@ -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()
|
@ -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()
|
@ -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()
|
@ -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()
|
@ -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()
|
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 6.6 KiB |
@ -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 |
Before Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 124 KiB |
Before Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 19 KiB |