1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
|
""" Camera class similar to that of camera.py, this time implemented as a graphics Group.
Interface is much more simplified, with only a position and zoom implemented, but is easily
extended to add other features such as autoscroll.
camera = CameraGroup(x=0, y=0, zoom=1)
world_object = pyglet.some_renderable(batch=batch, group=camera)
ui_object = pyglet.some_renderable(batch=batch) # <-- Using the same batch here
@window.event
def on_draw():
window.clear()
batch.draw() # Only one batch necessary
A centered camera class is also provided, where the position of the camera is the center of
the screen instead of the bottom left.
centered_camera = CenteredCameraGroup(window, x=0, y=0, zoom=1)
Demo:
Use arrow keys to move the camera around the scene.
Note that everything in the window can be added to the same batch, as a group is used to
seperate things in world space from things in "UI" space.
"""
import pyglet
from pyglet.graphics import Group
from pyglet.math import Vec2
from typing import Optional
class CameraGroup(Group):
""" Graphics group emulating the behaviour of a camera in 2D space. """
def __init__(
self,
x: float, y: float,
zoom: float = 1.0,
parent: Optional[Group] = None
):
super().__init__(parent)
self.x, self.y = x, y
self.zoom = zoom
@property
def position(self) -> Vec2:
"""Query the current offset."""
return Vec2(self.x, self.y)
@position.setter
def position(self, new_position: Vec2):
"""Set the scroll offset directly."""
self.x, self.y = new_position
def set_state(self):
""" Apply zoom and camera offset to view matrix. """
pyglet.gl.glTranslatef(
-self.x * self.zoom,
-self.y * self.zoom,
0
)
# Scale with zoom
pyglet.gl.glScalef(self.zoom, self.zoom, 1)
def unset_state(self):
""" Revert zoom and camera offset from view matrix. """
# Since this is a matrix, you will need to reverse the translate after rendering otherwise
# it will multiply the current offset every draw update pushing it further and further away.
# Use inverse zoom to reverse zoom
pyglet.gl.glScalef(1 / self.zoom, 1 / self.zoom, 1)
# Reverse the translation
pyglet.gl.glTranslatef(
self.x * self.zoom,
self.y * self.zoom,
0
)
class CenteredCameraGroup(CameraGroup):
""" Alternative centered camera group.
(0, 0) will be the center of the screen, as opposed to the bottom left.
"""
def __init__(self, window: pyglet.window.Window, *args, **kwargs):
self.window = window
super().__init__(*args, **kwargs)
def set_state(self):
# Get our center offset, aka half the window dimensions
center_offset_x = self.window.width // 2
center_offset_y = self.window.height // 2
# Translate almost the same as normal, but add the center offset
pyglet.gl.glTranslatef(
-self.x * self.zoom + center_offset_x,
-self.y * self.zoom + center_offset_y,
0
)
# Scale like normal
pyglet.gl.glScalef(self.zoom, self.zoom, 1)
def unset_state(self):
# Get our center offset, aka half the window dimensions, because we are reversing the transform
# we use the negative dimensions here.
center_offset_x = -self.window.width // 2
center_offset_y = -self.window.height // 2
pyglet.gl.glScalef(1 / self.zoom, 1 / self.zoom, 1)
# Reverse the translation including center offset
pyglet.gl.glTranslatef(
self.x * self.zoom + center_offset_x,
self.y * self.zoom + center_offset_y,
0
)
if __name__ == "__main__":
from pyglet.window import key
# Create a window and a batch
window = pyglet.window.Window(resizable=True)
batch = pyglet.graphics.Batch()
# Key handler for movement
keys = key.KeyStateHandler()
window.push_handlers(keys)
# Use centered
camera = CenteredCameraGroup(window, 0, 0)
# Use un-centered
# camera = CameraGroup(0, 0)
# Create a scene
rect = pyglet.shapes.Rectangle(-25, -25, 50, 50, batch=batch, group=camera)
text = pyglet.text.Label("Text works too!", x=0, y=-50, anchor_x="center", batch=batch, group=camera)
# Create some "UI"
ui_text = pyglet.text.Label(
"Simply don't add to the group to make UI static (like this)",
anchor_y="bottom", batch=batch
)
position_text = pyglet.text.Label(
"",
x=window.width,
anchor_x="right", anchor_y="bottom",
batch=batch
)
@window.event
def on_draw():
# Draw our scene
window.clear()
batch.draw()
@window.event
def on_resize(width: float, height: float):
# Keep position text label to the right
position_text.x = width
def on_update(dt: float):
# Move camera with arrow keys
if keys[key.UP]:
camera.y += 50*dt
if keys[key.DOWN]:
camera.y -= 50*dt
if keys[key.LEFT]:
camera.x -= 50*dt
if keys[key.RIGHT]:
camera.x += 50*dt
# Update position text label
position_text.text = repr(round(camera.position))
# Start the demo
pyglet.clock.schedule(on_update)
pyglet.app.run()
|