From 886f7722506103b1ac120b268544cd9c20577556 Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Tue, 14 Feb 2012 21:20:16 -0500 Subject: [PATCH 01/45] Update README --- README | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README b/README index e69de29b..79a3100e 100644 --- a/README +++ b/README @@ -0,0 +1,3 @@ +Simple Minecraft-inspired demo written in Python and Pyglet + +Video: http://www.youtube.com/watch?v=kC3lwK631X8 From a81e11aaa49aac578b572935a19938fd2e4af48c Mon Sep 17 00:00:00 2001 From: H34l0r <14Muella@googlemail.com> Date: Tue, 23 Oct 2012 19:23:10 +1000 Subject: [PATCH 02/45] Jump height and mouse sensitivity in main.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed the jump height to 0.65. This makes it a whole lot easier to jump onto blocks. Changed the mouse sensitivity to 0.15. Having the mouse sensitivity too much higher than this is frustrating for the user, because the view window moves too quickly. --- main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index 7476e291..e36fa87b 100644 --- a/main.py +++ b/main.py @@ -358,7 +358,7 @@ def on_mouse_press(self, x, y, button, modifiers): self.set_exclusive_mouse(True) def on_mouse_motion(self, x, y, dx, dy): if self.exclusive: - m = 0.5 + m = 0.15 x, y = self.rotation x, y = x + dx * m, y + dy * m y = max(-90, min(90, y)) @@ -374,7 +374,7 @@ def on_key_press(self, symbol, modifiers): self.strafe[1] += 1 elif symbol == key.SPACE: if self.dy == 0: - self.dy = 0.05 + self.dy = 0.065 elif symbol == key.ESCAPE: self.set_exclusive_mouse(False) elif symbol == key.TAB: From 92356317b25c36c0fd73929ccb6b9e665d02a260 Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Sun, 3 Feb 2013 15:25:19 -0500 Subject: [PATCH 03/45] added LICENSE --- LICENSE | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..ac1f3b33 --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright (C) 2013 Michael Fogleman + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From 6e7f962125cc0f50351c5892620561433b6435fe Mon Sep 17 00:00:00 2001 From: blain maguire Date: Tue, 5 Mar 2013 17:03:41 +0000 Subject: [PATCH 04/45] Fixed ctypes error and load directly from PNG Added ctypes import to fix an error on my machine. Texture is now imported directly from PNG (removed base64 string code, included encode.py as it is not needed). --- encode.py | 33 --------------------------------- main.py | 6 ++---- 2 files changed, 2 insertions(+), 37 deletions(-) delete mode 100644 encode.py diff --git a/encode.py b/encode.py deleted file mode 100644 index 96811c57..00000000 --- a/encode.py +++ /dev/null @@ -1,33 +0,0 @@ -import os -import base64 - -EXTENSIONS = [ - '.png', -] - -def print_data(data): - size = 72 - offset = 0 - length = len(data) - while offset < length: - print ' "%s"' % data[offset:offset+size] - offset += size - -def generate(folder): - print '# Automatically generated file!' - print 'from wx.lib.embeddedimage import PyEmbeddedImage' - print - for name in os.listdir(folder): - if name[-4:] not in EXTENSIONS: - continue - path = os.path.join(folder, name) - base = name[:-4] - with open(path, 'rb') as f: - encoded = base64.b64encode(f.read()) - print '%s = PyEmbeddedImage(' % base - print_data(encoded) - print ')' - print - -if __name__ == '__main__': - generate('.') diff --git a/main.py b/main.py index e36fa87b..271abc50 100644 --- a/main.py +++ b/main.py @@ -1,10 +1,9 @@ from pyglet.gl import * from pyglet.window import key -import StringIO -import base64 import math import random import time +from ctypes import c_float SECTOR_SIZE = 16 @@ -51,8 +50,7 @@ def tex_coords(top, bottom, side): class TextureGroup(pyglet.graphics.Group): def __init__(self, data): super(TextureGroup, self).__init__() - fp = StringIO.StringIO(base64.b64decode(data)) - self.texture = pyglet.image.load('__file__.png', file=fp).get_texture() + self.texture = pyglet.image.load('texture.png').get_texture() def set_state(self): glEnable(self.texture.target) glBindTexture(self.texture.target, self.texture.id) From 50247398b898bd6de3bbb8fd4b9e8c9becc1bb70 Mon Sep 17 00:00:00 2001 From: Yuval Greenfield Date: Fri, 29 Mar 2013 14:42:48 +0300 Subject: [PATCH 05/45] A more majestic jump/gravity combination Minecraft jumps are a little slower. --- main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main.py b/main.py index e36fa87b..866f2192 100644 --- a/main.py +++ b/main.py @@ -306,8 +306,8 @@ def _update(self, dt): dx, dy, dz = dx * d, dy * d, dz * d # gravity if not self.flying: - self.dy -= dt / 4 - self.dy = max(self.dy, -0.5) + self.dy -= dt * 0.044 # g force, should be = jump_speed * 0.5 / max_jump_height + self.dy = max(self.dy, -0.5) # terminal velocity dy += self.dy # collisions x, y, z = self.position @@ -374,7 +374,7 @@ def on_key_press(self, symbol, modifiers): self.strafe[1] += 1 elif symbol == key.SPACE: if self.dy == 0: - self.dy = 0.065 + self.dy = 0.015 # jump speed elif symbol == key.ESCAPE: self.set_exclusive_mouse(False) elif symbol == key.TAB: From 06133b00b0e90d797c048ac1b824c0f9df619d47 Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Fri, 29 Mar 2013 08:31:31 -0400 Subject: [PATCH 06/45] removed TEXTURE_DATA, just load it from file --- main.py | 114 ++------------------------------------------------------ 1 file changed, 3 insertions(+), 111 deletions(-) diff --git a/main.py b/main.py index 271abc50..2cde80b8 100644 --- a/main.py +++ b/main.py @@ -1,9 +1,9 @@ from pyglet.gl import * from pyglet.window import key +from ctypes import c_float import math import random import time -from ctypes import c_float SECTOR_SIZE = 16 @@ -48,7 +48,7 @@ def tex_coords(top, bottom, side): ] class TextureGroup(pyglet.graphics.Group): - def __init__(self, data): + def __init__(self): super(TextureGroup, self).__init__() self.texture = pyglet.image.load('texture.png').get_texture() def set_state(self): @@ -70,7 +70,7 @@ def sectorize(position): class Model(object): def __init__(self): self.batch = pyglet.graphics.Batch() - self.group = TextureGroup(TEXTURE_DATA) + self.group = TextureGroup() self.world = {} self.shown = {} self._shown = {} @@ -460,113 +460,5 @@ def main(): setup() pyglet.app.run() -TEXTURE_DATA = ( - "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" - "gg==" -) - if __name__ == '__main__': main() From ffdd5424d24279deab630b414f69812b7a9586b8 Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Fri, 29 Mar 2013 08:43:06 -0400 Subject: [PATCH 07/45] TextureGroup path argument --- main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main.py b/main.py index b4d4dd46..f35096c4 100644 --- a/main.py +++ b/main.py @@ -48,9 +48,9 @@ def tex_coords(top, bottom, side): ] class TextureGroup(pyglet.graphics.Group): - def __init__(self): + def __init__(self, path): super(TextureGroup, self).__init__() - self.texture = pyglet.image.load('texture.png').get_texture() + self.texture = pyglet.image.load(path).get_texture() def set_state(self): glEnable(self.texture.target) glBindTexture(self.texture.target, self.texture.id) @@ -70,7 +70,7 @@ def sectorize(position): class Model(object): def __init__(self): self.batch = pyglet.graphics.Batch() - self.group = TextureGroup() + self.group = TextureGroup('texture.png') self.world = {} self.shown = {} self._shown = {} From 7106671b10592bc89143578fcd0ad136f0827fbb Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Fri, 29 Mar 2013 08:50:23 -0400 Subject: [PATCH 08/45] added draw_focused_block --- main.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/main.py b/main.py index f35096c4..d1f9d03f 100644 --- a/main.py +++ b/main.py @@ -425,9 +425,20 @@ def on_draw(self): self.set_3d() glColor3d(1, 1, 1) self.model.batch.draw() + self.draw_focused_block() self.set_2d() self.draw_label() self.draw_reticle() + def draw_focused_block(self): + vector = self.get_sight_vector() + block = self.model.hit_test(self.position, vector)[0] + if block: + x, y, z = block + vertex_data = cube_vertices(x, y, z, 0.51) + glColor3d(0, 0, 0) + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) + pyglet.graphics.draw(24, GL_QUADS, ('v3f/static', vertex_data)) + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) def draw_label(self): x, y, z = self.position self.label.text = '%02d (%.2f, %.2f, %.2f) %d / %d' % ( From c7fe87c43b7fec820d2d9264c9ba4df7543882cd Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Fri, 29 Mar 2013 09:33:12 -0400 Subject: [PATCH 09/45] markdown readme --- README => README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) rename README => README.md (52%) diff --git a/README b/README.md similarity index 52% rename from README rename to README.md index 79a3100e..4f2ec6cc 100644 --- a/README +++ b/README.md @@ -1,3 +1,5 @@ +# Minecraft + Simple Minecraft-inspired demo written in Python and Pyglet -Video: http://www.youtube.com/watch?v=kC3lwK631X8 +http://www.youtube.com/watch?v=kC3lwK631X8 From 915700d390e41b804e2256bbc8a57e6c7eeadf3e Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Fri, 29 Mar 2013 10:36:19 -0300 Subject: [PATCH 10/45] Update README.md --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4f2ec6cc..549b98b2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,16 @@ # Minecraft -Simple Minecraft-inspired demo written in Python and Pyglet +Simple Minecraft-inspired demo written in Python and Pyglet. http://www.youtube.com/watch?v=kC3lwK631X8 + +## How to Run + + pip install pyglet + git clone https://github.com/fogleman/Minecraft.git + cd Minecraft + python main.py + +On Mac OS X, you may have an issue with running Pyglet in 64-bit mode. Try this... + + arch -i386 python main.py From 818cc44e41f7a0b2e7f3cc7199b2f8cce32d4658 Mon Sep 17 00:00:00 2001 From: jminardi Date: Sat, 30 Mar 2013 02:30:34 -0500 Subject: [PATCH 11/45] pep8 --- main.py | 93 ++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 18 deletions(-) diff --git a/main.py b/main.py index d1f9d03f..60d22a20 100644 --- a/main.py +++ b/main.py @@ -5,24 +5,29 @@ import random import time + +# Size of sectors used to each block loading. SECTOR_SIZE = 16 + def cube_vertices(x, y, z, n): return [ - x-n,y+n,z-n, x-n,y+n,z+n, x+n,y+n,z+n, x+n,y+n,z-n, # top - x-n,y-n,z-n, x+n,y-n,z-n, x+n,y-n,z+n, x-n,y-n,z+n, # bottom - x-n,y-n,z-n, x-n,y-n,z+n, x-n,y+n,z+n, x-n,y+n,z-n, # left - x+n,y-n,z+n, x+n,y-n,z-n, x+n,y+n,z-n, x+n,y+n,z+n, # right - x-n,y-n,z+n, x+n,y-n,z+n, x+n,y+n,z+n, x-n,y+n,z+n, # front - x+n,y-n,z-n, x-n,y-n,z-n, x-n,y+n,z-n, x+n,y+n,z-n, # back + x-n,y+n,z-n, x-n,y+n,z+n, x+n,y+n,z+n, x+n,y+n,z-n, # top + x-n,y-n,z-n, x+n,y-n,z-n, x+n,y-n,z+n, x-n,y-n,z+n, # bottom + x-n,y-n,z-n, x-n,y-n,z+n, x-n,y+n,z+n, x-n,y+n,z-n, # left + x+n,y-n,z+n, x+n,y-n,z-n, x+n,y+n,z-n, x+n,y+n,z+n, # right + x-n,y-n,z+n, x+n,y-n,z+n, x+n,y+n,z+n, x-n,y+n,z+n, # front + x+n,y-n,z-n, x-n,y-n,z-n, x-n,y+n,z-n, x+n,y+n,z-n, # back ] + def tex_coord(x, y, n=4): m = 1.0 / n dx = x * m dy = y * m return dx, dy, dx + m, dy, dx + m, dy + m, dx, dy + m + def tex_coords(top, bottom, side): top = tex_coord(*top) bottom = tex_coord(*bottom) @@ -33,6 +38,7 @@ def tex_coords(top, bottom, side): result.extend(side * 4) return result + GRASS = tex_coords((1, 0), (0, 1), (0, 0)) SAND = tex_coords((1, 1), (1, 1), (1, 1)) BRICK = tex_coords((2, 0), (2, 0), (2, 0)) @@ -47,27 +53,35 @@ def tex_coords(top, bottom, side): ( 0, 0,-1), ] + class TextureGroup(pyglet.graphics.Group): + def __init__(self, path): super(TextureGroup, self).__init__() self.texture = pyglet.image.load(path).get_texture() + def set_state(self): glEnable(self.texture.target) glBindTexture(self.texture.target, self.texture.id) + def unset_state(self): glDisable(self.texture.target) + def normalize(position): x, y, z = position x, y, z = (int(round(x)), int(round(y)), int(round(z))) return (x, y, z) + def sectorize(position): x, y, z = normalize(position) x, y, z = x / SECTOR_SIZE, y / SECTOR_SIZE, z / SECTOR_SIZE return (x, 0, z) + class Model(object): + def __init__(self): self.batch = pyglet.graphics.Batch() self.group = TextureGroup('texture.png') @@ -77,6 +91,7 @@ def __init__(self): self.sectors = {} self.queue = [] self.initialize() + def initialize(self): n = 80 s = 1 @@ -106,6 +121,7 @@ def initialize(self): continue self.init_block((x, y, z), t) s -= d + def hit_test(self, position, vector, max_distance=8): m = 8 x, y, z = position @@ -118,14 +134,17 @@ def hit_test(self, position, vector, max_distance=8): previous = key x, y, z = x + dx / m, y + dy / m, z + dz / m return None, None + def exposed(self, position): x, y, z = position for dx, dy, dz in FACES: if (x + dx, y + dy, z + dz) not in self.world: return True return False + def init_block(self, position, texture): self.add_block(position, texture, False) + def add_block(self, position, texture, sync=True): if position in self.world: self.remove_block(position, sync) @@ -135,6 +154,7 @@ def add_block(self, position, texture, sync=True): if self.exposed(position): self.show_block(position) self.check_neighbors(position) + def remove_block(self, position, sync=True): del self.world[position] self.sectors[sectorize(position)].remove(position) @@ -142,6 +162,7 @@ def remove_block(self, position, sync=True): if position in self.shown: self.hide_block(position) self.check_neighbors(position) + def check_neighbors(self, position): x, y, z = position for dx, dy, dz in FACES: @@ -154,10 +175,12 @@ def check_neighbors(self, position): else: if key in self.shown: self.hide_block(key) + def show_blocks(self): for position in self.world: if position not in self.shown and self.exposed(position): self.show_block(position) + def show_block(self, position, immediate=True): texture = self.world[position] self.shown[position] = texture @@ -165,6 +188,7 @@ def show_block(self, position, immediate=True): self._show_block(position, texture) else: self.enqueue(self._show_block, position, texture) + def _show_block(self, position, texture): x, y, z = position # only show exposed faces @@ -172,7 +196,7 @@ def _show_block(self, position, texture): count = 24 vertex_data = cube_vertices(x, y, z, 0.5) texture_data = list(texture) - for dx, dy, dz in []:#FACES: + for dx, dy, dz in []: # FACES: if (x + dx, y + dy, z + dz) in self.world: count -= 4 i = index * 12 @@ -182,31 +206,36 @@ def _show_block(self, position, texture): else: index += 1 # create vertex list - self._shown[position] = self.batch.add(count, GL_QUADS, self.group, + self._shown[position] = self.batch.add(count, GL_QUADS, self.group, ('v3f/static', vertex_data), ('t2f/static', texture_data)) + def hide_block(self, position, immediate=True): self.shown.pop(position) if immediate: self._hide_block(position) else: self.enqueue(self._hide_block, position) + def _hide_block(self, position): self._shown.pop(position).delete() + def show_sector(self, sector): for position in self.sectors.get(sector, []): if position not in self.shown and self.exposed(position): self.show_block(position, False) + def hide_sector(self, sector): for position in self.sectors.get(sector, []): if position in self.shown: self.hide_block(position, False) + def change_sectors(self, before, after): before_set = set() after_set = set() pad = 4 for dx in xrange(-pad, pad + 1): - for dy in [0]: # xrange(-pad, pad + 1): + for dy in [0]: # xrange(-pad, pad + 1): for dz in xrange(-pad, pad + 1): if dx ** 2 + dy ** 2 + dz ** 2 > (pad + 1) ** 2: continue @@ -222,20 +251,26 @@ def change_sectors(self, before, after): self.show_sector(sector) for sector in hide: self.hide_sector(sector) + def enqueue(self, func, *args): self.queue.append((func, args)) + def dequeue(self): func, args = self.queue.pop(0) func(*args) + def process_queue(self): start = time.clock() while self.queue and time.clock() - start < 1 / 60.0: self.dequeue() + def process_entire_queue(self): while self.queue: self.dequeue() + class Window(pyglet.window.Window): + def __init__(self, *args, **kwargs): super(Window, self).__init__(*args, **kwargs) self.exclusive = False @@ -247,13 +282,15 @@ def __init__(self, *args, **kwargs): self.reticle = None self.dy = 0 self.model = Model() - self.label = pyglet.text.Label('', font_name='Arial', font_size=18, - x=10, y=self.height - 10, anchor_x='left', anchor_y='top', + self.label = pyglet.text.Label('', font_name='Arial', font_size=18, + x=10, y=self.height - 10, anchor_x='left', anchor_y='top', color=(0, 0, 0, 255)) pyglet.clock.schedule_interval(self.update, 1.0 / 60) + def set_exclusive_mouse(self, exclusive): super(Window, self).set_exclusive_mouse(exclusive) self.exclusive = exclusive + def get_sight_vector(self): x, y = self.rotation m = math.cos(math.radians(y)) @@ -261,6 +298,7 @@ def get_sight_vector(self): dx = math.cos(math.radians(x - 90)) * m dz = math.sin(math.radians(x - 90)) * m return (dx, dy, dz) + def get_motion_vector(self): if any(self.strafe): x, y = self.rotation @@ -284,6 +322,7 @@ def get_motion_vector(self): dx = 0.0 dz = 0.0 return (dx, dy, dz) + def update(self, dt): self.model.process_queue() sector = sectorize(self.position) @@ -296,6 +335,7 @@ def update(self, dt): dt = min(dt, 0.2) for _ in xrange(m): self._update(dt / m) + def _update(self, dt): # walking speed = 15 if self.flying else 5 @@ -304,25 +344,26 @@ def _update(self, dt): dx, dy, dz = dx * d, dy * d, dz * d # gravity if not self.flying: - self.dy -= dt * 0.044 # g force, should be = jump_speed * 0.5 / max_jump_height - self.dy = max(self.dy, -0.5) # terminal velocity + self.dy -= dt * 0.044 # g force, should be = jump_speed * 0.5 / max_jump_height + self.dy = max(self.dy, -0.5) # terminal velocity dy += self.dy # collisions x, y, z = self.position x, y, z = self.collide((x + dx, y + dy, z + dz), 2) self.position = (x, y, z) + def collide(self, position, height): pad = 0.25 p = list(position) np = normalize(position) - for face in FACES: # check all surrounding blocks - for i in xrange(3): # check each dimension independently + for face in FACES: # check all surrounding blocks + for i in xrange(3): # check each dimension independently if not face[i]: continue d = (p[i] - np[i]) * face[i] if d < pad: continue - for dy in xrange(height): # check each height + for dy in xrange(height): # check each height op = list(np) op[1] -= dy op[i] += face[i] @@ -334,12 +375,14 @@ def collide(self, position, height): self.dy = 0 break return tuple(p) + def on_mouse_scroll(self, x, y, scroll_x, scroll_y): return x, y, z = self.position dx, dy, dz = self.get_sight_vector() d = scroll_y * 10 self.position = (x + dx * d, y + dy * d, z + dz * d) + def on_mouse_press(self, x, y, button, modifiers): if self.exclusive: vector = self.get_sight_vector() @@ -354,6 +397,7 @@ def on_mouse_press(self, x, y, button, modifiers): self.model.add_block(previous, BRICK) else: self.set_exclusive_mouse(True) + def on_mouse_motion(self, x, y, dx, dy): if self.exclusive: m = 0.15 @@ -361,6 +405,7 @@ def on_mouse_motion(self, x, y, dx, dy): x, y = x + dx * m, y + dy * m y = max(-90, min(90, y)) self.rotation = (x, y) + def on_key_press(self, symbol, modifiers): if symbol == key.W: self.strafe[0] -= 1 @@ -372,11 +417,12 @@ def on_key_press(self, symbol, modifiers): self.strafe[1] += 1 elif symbol == key.SPACE: if self.dy == 0: - self.dy = 0.015 # jump speed + self.dy = 0.015 # jump speed elif symbol == key.ESCAPE: self.set_exclusive_mouse(False) elif symbol == key.TAB: self.flying = not self.flying + def on_key_release(self, symbol, modifiers): if symbol == key.W: self.strafe[0] += 1 @@ -386,6 +432,7 @@ def on_key_release(self, symbol, modifiers): self.strafe[1] += 1 elif symbol == key.D: self.strafe[1] -= 1 + def on_resize(self, width, height): # label self.label.y = height - 10 @@ -397,6 +444,7 @@ def on_resize(self, width, height): self.reticle = pyglet.graphics.vertex_list(4, ('v2i', (x - n, y, x + n, y, x, y - n, x, y + n)) ) + def set_2d(self): width, height = self.get_size() glDisable(GL_DEPTH_TEST) @@ -406,6 +454,7 @@ def set_2d(self): glOrtho(0, width, 0, height, -1, 1) glMatrixMode(GL_MODELVIEW) glLoadIdentity() + def set_3d(self): width, height = self.get_size() glEnable(GL_DEPTH_TEST) @@ -420,6 +469,7 @@ def set_3d(self): glRotatef(-y, math.cos(math.radians(x)), 0, math.sin(math.radians(x))) x, y, z = self.position glTranslatef(-x, -y, -z) + def on_draw(self): self.clear() self.set_3d() @@ -429,6 +479,7 @@ def on_draw(self): self.set_2d() self.draw_label() self.draw_reticle() + def draw_focused_block(self): vector = self.get_sight_vector() block = self.model.hit_test(self.position, vector)[0] @@ -439,16 +490,19 @@ def draw_focused_block(self): glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) pyglet.graphics.draw(24, GL_QUADS, ('v3f/static', vertex_data)) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) + def draw_label(self): x, y, z = self.position self.label.text = '%02d (%.2f, %.2f, %.2f) %d / %d' % ( - pyglet.clock.get_fps(), x, y, z, + pyglet.clock.get_fps(), x, y, z, len(self.model._shown), len(self.model.world)) self.label.draw() + def draw_reticle(self): glColor3d(0, 0, 0) self.reticle.draw(GL_LINES) + def setup_fog(): glEnable(GL_FOG) glFogfv(GL_FOG_COLOR, (c_float * 4)(0.53, 0.81, 0.98, 1)) @@ -458,6 +512,7 @@ def setup_fog(): glFogf(GL_FOG_START, 20.0) glFogf(GL_FOG_END, 60.0) + def setup(): glClearColor(0.53, 0.81, 0.98, 1) glEnable(GL_CULL_FACE) @@ -465,11 +520,13 @@ def setup(): glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) setup_fog() + def main(): window = Window(width=800, height=600, caption='Pyglet', resizable=True) window.set_exclusive_mouse(True) setup() pyglet.app.run() + if __name__ == '__main__': main() From 7042d8cb277aed7bdd4c875f9ff647bff7156898 Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Sat, 30 Mar 2013 20:24:41 -0400 Subject: [PATCH 12/45] use GLfloat instead of c_float --- main.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/main.py b/main.py index d1f9d03f..a454868d 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,5 @@ from pyglet.gl import * from pyglet.window import key -from ctypes import c_float import math import random import time @@ -451,7 +450,7 @@ def draw_reticle(self): def setup_fog(): glEnable(GL_FOG) - glFogfv(GL_FOG_COLOR, (c_float * 4)(0.53, 0.81, 0.98, 1)) + glFogfv(GL_FOG_COLOR, (GLfloat * 4)(0.53, 0.81, 0.98, 1)) glHint(GL_FOG_HINT, GL_DONT_CARE) glFogi(GL_FOG_MODE, GL_LINEAR) glFogf(GL_FOG_DENSITY, 0.35) From e825d879f9c1cd9b395451187297780592fea83b Mon Sep 17 00:00:00 2001 From: geppettodivacin Date: Sat, 30 Mar 2013 23:08:19 -0500 Subject: [PATCH 13/45] Added support for creating all types of breakable blocks. --- main.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index a454868d..a855b608 100644 --- a/main.py +++ b/main.py @@ -245,6 +245,9 @@ def __init__(self, *args, **kwargs): self.sector = None self.reticle = None self.dy = 0 + self.inventory = [BRICK, GRASS, SAND] + self.block = self.inventory[0] + self.num_keys = [key._0, key._1, key._2, key._3, key._4, key._5, key._6, key._7, key._8, key._9] self.model = Model() self.label = pyglet.text.Label('', font_name='Arial', font_size=18, x=10, y=self.height - 10, anchor_x='left', anchor_y='top', @@ -350,7 +353,7 @@ def on_mouse_press(self, x, y, button, modifiers): self.model.remove_block(block) else: if previous: - self.model.add_block(previous, BRICK) + self.model.add_block(previous, self.block) else: self.set_exclusive_mouse(True) def on_mouse_motion(self, x, y, dx, dy): @@ -376,6 +379,8 @@ def on_key_press(self, symbol, modifiers): self.set_exclusive_mouse(False) elif symbol == key.TAB: self.flying = not self.flying + elif symbol in self.num_keys: + self.block = self.inventory[(symbol - self.num_keys[0]) % len(self.inventory)] def on_key_release(self, symbol, modifiers): if symbol == key.W: self.strafe[0] += 1 From 6c348ae97adabac3e234295c40aa5d2617cf1eb9 Mon Sep 17 00:00:00 2001 From: Adam McKerlie Date: Sun, 31 Mar 2013 09:10:44 -0400 Subject: [PATCH 14/45] Use Pyglet 1.2 for OS X --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 549b98b2..7b8eead3 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,6 @@ http://www.youtube.com/watch?v=kC3lwK631X8 cd Minecraft python main.py -On Mac OS X, you may have an issue with running Pyglet in 64-bit mode. Try this... +Pyglet 1.1.4 can't run in 64-bit mode. Using Pyglet 1.2 fixes this problem. - arch -i386 python main.py + pip install https://pyglet.googlecode.com/files/pyglet-1.2alpha1.tar.gz From f5827045a0022a04cba25bda68dec73989d90b6e Mon Sep 17 00:00:00 2001 From: Piotr Grabowski Date: Sun, 31 Mar 2013 18:22:36 +0200 Subject: [PATCH 15/45] Tweaked textures to be more vibrant and simillar to original Minecraft I've tweaked textures to be more contrast and better-looking with fog. Sand texture is now more vibrant and distinguishable from far. Sky color and textures are more realistic and similar to original Minecraft. --- main.py | 4 ++-- texture.png | Bin 5617 -> 5257 bytes 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index a454868d..230f4780 100644 --- a/main.py +++ b/main.py @@ -450,7 +450,7 @@ def draw_reticle(self): def setup_fog(): glEnable(GL_FOG) - glFogfv(GL_FOG_COLOR, (GLfloat * 4)(0.53, 0.81, 0.98, 1)) + glFogfv(GL_FOG_COLOR, (GLfloat * 4)(0.5, 0.69, 1.0, 1)) glHint(GL_FOG_HINT, GL_DONT_CARE) glFogi(GL_FOG_MODE, GL_LINEAR) glFogf(GL_FOG_DENSITY, 0.35) @@ -458,7 +458,7 @@ def setup_fog(): glFogf(GL_FOG_END, 60.0) def setup(): - glClearColor(0.53, 0.81, 0.98, 1) + glClearColor(0.5, 0.69, 1.0, 1) glEnable(GL_CULL_FACE) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) diff --git a/texture.png b/texture.png index 0139d7cb015f5b2fb3329813b049a0e34cf8d914..9d05a7e2db1eecfe427228730ba1a9d7f5e38515 100644 GIT binary patch literal 5257 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 Date: Sun, 31 Mar 2013 13:34:59 -0400 Subject: [PATCH 16/45] Suggest multiple ways to run in OS X --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b8eead3..c26ffdc2 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,13 @@ http://www.youtube.com/watch?v=kC3lwK631X8 cd Minecraft python main.py -Pyglet 1.1.4 can't run in 64-bit mode. Using Pyglet 1.2 fixes this problem. + +On Mac OS X, you may have an issue with running Pyglet in 64-bit mode. Try this running python in 32-bit mode first. + + arch -i386 python main.py + +or try Pyglet 1.2 which supports 64-bit mode pip install https://pyglet.googlecode.com/files/pyglet-1.2alpha1.tar.gz + + From 5d6190b600f5fc98d16e7b459b55d44a5860fef7 Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Sun, 31 Mar 2013 19:49:26 -0400 Subject: [PATCH 17/45] keys 1 - 0, not 0 - 9 --- main.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index 06470e0d..411ab78a 100644 --- a/main.py +++ b/main.py @@ -247,7 +247,9 @@ def __init__(self, *args, **kwargs): self.dy = 0 self.inventory = [BRICK, GRASS, SAND] self.block = self.inventory[0] - self.num_keys = [key._0, key._1, key._2, key._3, key._4, key._5, key._6, key._7, key._8, key._9] + self.num_keys = [ + key._1, key._2, key._3, key._4, key._5, + key._6, key._7, key._8, key._9, key._0] self.model = Model() self.label = pyglet.text.Label('', font_name='Arial', font_size=18, x=10, y=self.height - 10, anchor_x='left', anchor_y='top', @@ -380,7 +382,8 @@ def on_key_press(self, symbol, modifiers): elif symbol == key.TAB: self.flying = not self.flying elif symbol in self.num_keys: - self.block = self.inventory[(symbol - self.num_keys[0]) % len(self.inventory)] + index = (symbol - self.num_keys[0]) % len(self.inventory) + self.block = self.inventory[index] def on_key_release(self, symbol, modifiers): if symbol == key.W: self.strafe[0] += 1 From 8bc3605f26a4f428bed4ea1fa31ee62654c2a44e Mon Sep 17 00:00:00 2001 From: richvel Date: Mon, 1 Apr 2013 11:09:33 +0100 Subject: [PATCH 18/45] How to play instructions in README Key bindings and mouse actions --- README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/README.md b/README.md index 549b98b2..18d81e20 100644 --- a/README.md +++ b/README.md @@ -14,3 +14,27 @@ http://www.youtube.com/watch?v=kC3lwK631X8 On Mac OS X, you may have an issue with running Pyglet in 64-bit mode. Try this... arch -i386 python main.py + +## How to Play + +Moving: + +- W: forward +- S: back +- A: strafe left +- F: strafe right +- Mouse: look around +- Space: jump +- Tab: toggle flying mode + +Building: +- selecting type of block to create: + - 0: sand + - 1: grass + - 2: brick +- Mouse right-click: create block +- Mouse left- click: remove block + +Quitting: +- ESC: release mouse, then close window + From fae6988d441345efaa4c27d3c0ee833d5999a257 Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Mon, 1 Apr 2013 09:55:07 -0300 Subject: [PATCH 19/45] Update README.md --- README.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 18d81e20..ab9cc0ba 100644 --- a/README.md +++ b/README.md @@ -17,24 +17,25 @@ On Mac OS X, you may have an issue with running Pyglet in 64-bit mode. Try this. ## How to Play -Moving: +### Moving - W: forward - S: back - A: strafe left -- F: strafe right +- D: strafe right - Mouse: look around - Space: jump - Tab: toggle flying mode -Building: -- selecting type of block to create: - - 0: sand - - 1: grass - - 2: brick -- Mouse right-click: create block -- Mouse left- click: remove block +### Building -Quitting: -- ESC: release mouse, then close window +- Selecting type of block to create: + - 1: brick + - 2: grass + - 3: sand +- Mouse left-click: remove block +- Mouse right-click: create block + +### Quitting +- ESC: release mouse, then close window From e1dd181c3688bbf83dd515daf5d375da6855b419 Mon Sep 17 00:00:00 2001 From: richvel Date: Tue, 2 Apr 2013 13:48:39 +0100 Subject: [PATCH 20/45] README update for Mac 32-bit issue #7, and installing git+pip on various platforms --- README.md | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3054aa74..ca16aa96 100644 --- a/README.md +++ b/README.md @@ -11,14 +11,36 @@ http://www.youtube.com/watch?v=kC3lwK631X8 cd Minecraft python main.py -On Mac OS X, you may have an issue with running Pyglet in 64-bit mode. Try running Python in 32-bit mode first. +### Mac + +On Mac OS X, you may have an issue with running Pyglet in 64-bit mode. Try running Python in 32-bit mode first: arch -i386 python main.py -Or, try Pyglet 1.2 which supports 64-bit mode. +If that doesn't work, set Python to run in 32-bit mode by default: + + defaults write com.apple.versioner.python Prefer-32-Bit -bool yes + +This assumes you are using the OS X default Python. Works on Lion 10.7 with the default Python 2.7, and may work on other versions too. Please raise an issue if not. + +Or try Pyglet 1.2 alpha, which supports 64-bit mode: pip install https://pyglet.googlecode.com/files/pyglet-1.2alpha1.tar.gz +### If you don't have pip or git + +For pip: + +- Mac or Linux: install with `sudo easy_install pip` (Mac or Linux) - or find a package called something like 'python-pip' in Linux and install in your package manager. +- Windows: [http://stackoverflow.com/a/12476379/992887](install Distribute then Pip) using the linked .MSI installers. + +For git: + +- Mac: install [Homebrew](http://mxcl.github.com/homebrew/) first, then `brew install git`. +- Windows or Linux: see [Installing Git](http://git-scm.com/book/en/Getting-Started-Installing-Git) from the _Pro Git_ book. + +See the [https://github.com/fogleman/Minecraft/wiki](wiki) for this project to install Python, and other tips. + ## How to Play ### Moving From 77560c5968cf10d5370e7b572dbd3b0d60a2979e Mon Sep 17 00:00:00 2001 From: richvel Date: Tue, 2 Apr 2013 14:16:10 +0100 Subject: [PATCH 21/45] fixed link in README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ca16aa96..0784880b 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,8 @@ Or try Pyglet 1.2 alpha, which supports 64-bit mode: For pip: -- Mac or Linux: install with `sudo easy_install pip` (Mac or Linux) - or find a package called something like 'python-pip' in Linux and install in your package manager. -- Windows: [http://stackoverflow.com/a/12476379/992887](install Distribute then Pip) using the linked .MSI installers. +- Mac or Linux: install with `sudo easy_install pip` (Mac or Linux) - or (Linux) find a package called something like 'python-pip' in your package manager. +- Windows: [install Distribute then Pip](http://stackoverflow.com/a/12476379/992887) using the linked .MSI installers. For git: From 56a0e28849d098a0d6c39dc88e60400190f715ca Mon Sep 17 00:00:00 2001 From: richvel Date: Tue, 2 Apr 2013 14:49:49 +0100 Subject: [PATCH 22/45] another README link fix ... --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0784880b..29138b93 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ For git: - Mac: install [Homebrew](http://mxcl.github.com/homebrew/) first, then `brew install git`. - Windows or Linux: see [Installing Git](http://git-scm.com/book/en/Getting-Started-Installing-Git) from the _Pro Git_ book. -See the [https://github.com/fogleman/Minecraft/wiki](wiki) for this project to install Python, and other tips. +See the [wiki](https://github.com/fogleman/Minecraft/wiki) for this project to install Python, and other tips. ## How to Play From 92723e77dce56e75c51c29690ef0aaf206f852a8 Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Tue, 2 Apr 2013 11:00:26 -0300 Subject: [PATCH 23/45] Update README.md --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index 29138b93..c8267da6 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,28 @@ Simple Minecraft-inspired demo written in Python and Pyglet. http://www.youtube.com/watch?v=kC3lwK631X8 +## Goals and Vision + +I would like to see this project turn into an educational tool. Kids love Minecraft and Python is a great first language. +This is a good opportunity to get children excited about programming. + +The code should become well commented and more easily configurable. It should be easy to make some simple changes +and see the results quickly. + +I think it would be great to turn the project into more of a library / API... a Python package that you import and then +use / configure to setup a world and run it. Something along these lines... + + import mc + + world = mc.World(...) + world.set_block(x, y, z, mc.DIRT) + mc.run(world) + +The API could contain functionality for the following: + +- Easily configurable parameters like gravity, jump velocity, walking speed, etc. +- Hooks for terrain generation. + ## How to Run pip install pyglet From 07d069e38c6827b9671c59240fe0861fd96e5635 Mon Sep 17 00:00:00 2001 From: jminardi Date: Wed, 3 Apr 2013 01:18:13 -0500 Subject: [PATCH 24/45] pep8 and add doc strings --- main.py | 92 +++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 80 insertions(+), 12 deletions(-) diff --git a/main.py b/main.py index 60d22a20..8e000a73 100644 --- a/main.py +++ b/main.py @@ -6,11 +6,13 @@ import time -# Size of sectors used to each block loading. +# Size of sectors used to ease block loading. SECTOR_SIZE = 16 def cube_vertices(x, y, z, n): + """ Return the vertices of the cube at position x, y, z with size n. + """ return [ x-n,y+n,z-n, x-n,y+n,z+n, x+n,y+n,z+n, x+n,y+n,z-n, # top x-n,y-n,z-n, x+n,y-n,z-n, x+n,y-n,z+n, x-n,y-n,z+n, # bottom @@ -22,6 +24,8 @@ def cube_vertices(x, y, z, n): def tex_coord(x, y, n=4): + """ Return the bounding vertices of the texture square. + """ m = 1.0 / n dx = x * m dy = y * m @@ -29,6 +33,8 @@ def tex_coord(x, y, n=4): def tex_coords(top, bottom, side): + """ Return a list of the texture squares for the top, bottom and side. + """ top = tex_coord(*top) bottom = tex_coord(*bottom) side = tex_coord(*side) @@ -39,6 +45,8 @@ def tex_coords(top, bottom, side): return result +TEXTURE_PATH = 'texture.png' + GRASS = tex_coords((1, 0), (0, 1), (0, 0)) SAND = tex_coords((1, 1), (1, 1), (1, 1)) BRICK = tex_coords((2, 0), (2, 0), (2, 0)) @@ -69,12 +77,35 @@ def unset_state(self): def normalize(position): + """ Accepts `position` of arbitrary precision and returns the block + containing that position. + + Parameters + ---------- + position : tuple of len 3 + + Returns + ------- + block_position : tuple of ints of len 3 + + """ x, y, z = position x, y, z = (int(round(x)), int(round(y)), int(round(z))) return (x, y, z) def sectorize(position): + """ Returns a tuple representing the sector for the given `position`. + + Parameters + ---------- + position : tuple of len 3 + + Returns + ------- + sector : tuple of len 3 + + """ x, y, z = normalize(position) x, y, z = x / SECTOR_SIZE, y / SECTOR_SIZE, z / SECTOR_SIZE return (x, 0, z) @@ -83,26 +114,46 @@ def sectorize(position): class Model(object): def __init__(self): + texture = pyglet.image.load(TEXTURE_PATH).get_texture() + + # A Batch is a collection of vertex lists for batched rendering. self.batch = pyglet.graphics.Batch() + + # A TextureGroup manages an OpenGL texture. self.group = TextureGroup('texture.png') + + # A mapping from position to the texture of the block at that position. self.world = {} + + # Same mapping as `world` but only contains blocks that are shown. self.shown = {} + + # Mapping from position to a pyglet `VertextList` for all shown blocks. self._shown = {} + + # Mapping from sector to a list of positions inside that sector. self.sectors = {} + + # Simple function queue implemementation. self.queue = [] - self.initialize() - def initialize(self): - n = 80 - s = 1 - y = 0 + self._initialize() + + def _initialize(self): + n = 80 # 1/2 width and height of world + s = 1 # step size + y = 0 # initial y height for x in xrange(-n, n + 1, s): for z in xrange(-n, n + 1, s): + # create a layer stone an grass everywhere. self.init_block((x, y - 2, z), GRASS) self.init_block((x, y - 3, z), STONE) if x in (-n, n) or z in (-n, n): + # create outer walls. for dy in xrange(-2, 3): self.init_block((x, y + dy, z), STONE) + + # generate the hills randomly o = n - 10 for _ in xrange(120): a = random.randint(-o, o) @@ -123,6 +174,11 @@ def initialize(self): s -= d def hit_test(self, position, vector, max_distance=8): + """ Line of sight search from current position. If a block is + intersected it is returned, along with the block previously in the line + of sight. + + """ m = 8 x, y, z = position dx, dy, dz = vector @@ -146,6 +202,16 @@ def init_block(self, position, texture): self.add_block(position, texture, False) def add_block(self, position, texture, sync=True): + """ + Parameters + ---------- + position : tuple of len 3 + The (x, y, z) position of the block to add + texture : list or len 3 + The coordinates of the texture squares. Use `tex_coords()` to + generate + + """ if position in self.world: self.remove_block(position, sync) self.world[position] = texture @@ -187,7 +253,7 @@ def show_block(self, position, immediate=True): if immediate: self._show_block(position, texture) else: - self.enqueue(self._show_block, position, texture) + self._enqueue(self._show_block, position, texture) def _show_block(self, position, texture): x, y, z = position @@ -215,7 +281,7 @@ def hide_block(self, position, immediate=True): if immediate: self._hide_block(position) else: - self.enqueue(self._hide_block, position) + self._enqueue(self._hide_block, position) def _hide_block(self, position): self._shown.pop(position).delete() @@ -252,21 +318,21 @@ def change_sectors(self, before, after): for sector in hide: self.hide_sector(sector) - def enqueue(self, func, *args): + def _enqueue(self, func, *args): self.queue.append((func, args)) - def dequeue(self): + def _dequeue(self): func, args = self.queue.pop(0) func(*args) def process_queue(self): start = time.clock() while self.queue and time.clock() - start < 1 / 60.0: - self.dequeue() + self._dequeue() def process_entire_queue(self): while self.queue: - self.dequeue() + self._dequeue() class Window(pyglet.window.Window): @@ -324,6 +390,8 @@ def get_motion_vector(self): return (dx, dy, dz) def update(self, dt): + """ This method is scheduled to be called repeadetly. + """ self.model.process_queue() sector = sectorize(self.position) if sector != self.sector: From 059b80ac39ea2ea1ef691fd793e494e3b4d59f7a Mon Sep 17 00:00:00 2001 From: jminardi Date: Wed, 3 Apr 2013 01:33:18 -0500 Subject: [PATCH 25/45] remove TextureGroup definition --- main.py | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/main.py b/main.py index 96c07e6c..71fb99d0 100644 --- a/main.py +++ b/main.py @@ -1,9 +1,12 @@ -from pyglet.gl import * -from pyglet.window import key import math import random import time +from pyglet import image +from pyglet.gl import * +from pyglet.graphics import TextureGroup +from pyglet.window import key + # Size of sectors used to ease block loading. SECTOR_SIZE = 16 @@ -61,20 +64,6 @@ def tex_coords(top, bottom, side): ] -class TextureGroup(pyglet.graphics.Group): - - def __init__(self, path): - super(TextureGroup, self).__init__() - self.texture = pyglet.image.load(path).get_texture() - - def set_state(self): - glEnable(self.texture.target) - glBindTexture(self.texture.target, self.texture.id) - - def unset_state(self): - glDisable(self.texture.target) - - def normalize(position): """ Accepts `position` of arbitrary precision and returns the block containing that position. @@ -113,13 +102,12 @@ def sectorize(position): class Model(object): def __init__(self): - texture = pyglet.image.load(TEXTURE_PATH).get_texture() # A Batch is a collection of vertex lists for batched rendering. self.batch = pyglet.graphics.Batch() # A TextureGroup manages an OpenGL texture. - self.group = TextureGroup('texture.png') + self.group = TextureGroup(image.load(TEXTURE_PATH).get_texture()) # A mapping from position to the texture of the block at that position. self.world = {} From df11777b8ad85ffeafe3c5c4fbb671367023832d Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Wed, 3 Apr 2013 19:33:11 +0300 Subject: [PATCH 26/45] Fix jump Jumping sometimes fails to bring you up enough to climb a block. Increasing slightly the jump speed fixes this. --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index 411ab78a..ea0fbfe8 100644 --- a/main.py +++ b/main.py @@ -376,7 +376,7 @@ def on_key_press(self, symbol, modifiers): self.strafe[1] += 1 elif symbol == key.SPACE: if self.dy == 0: - self.dy = 0.015 # jump speed + self.dy = 0.016 # jump speed elif symbol == key.ESCAPE: self.set_exclusive_mouse(False) elif symbol == key.TAB: From a595b40b80865cfcd866bf0f305d58f3fee0a1e9 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Wed, 3 Apr 2013 18:57:12 +0200 Subject: [PATCH 27/45] Fix Python 3 compatibility. --- main.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index 411ab78a..da4acaf4 100644 --- a/main.py +++ b/main.py @@ -1,11 +1,17 @@ +from __future__ import division + from pyglet.gl import * from pyglet.window import key +import sys import math import random import time SECTOR_SIZE = 16 +if sys.version_info[0] >= 3: + xrange = range + def cube_vertices(x, y, z, n): return [ x-n,y+n,z-n, x-n,y+n,z+n, x+n,y+n,z+n, x+n,y+n,z-n, # top @@ -63,7 +69,7 @@ def normalize(position): def sectorize(position): x, y, z = normalize(position) - x, y, z = x / SECTOR_SIZE, y / SECTOR_SIZE, z / SECTOR_SIZE + x, y, z = x // SECTOR_SIZE, y // SECTOR_SIZE, z // SECTOR_SIZE return (x, 0, z) class Model(object): @@ -399,7 +405,7 @@ def on_resize(self, width, height): # reticle if self.reticle: self.reticle.delete() - x, y = self.width / 2, self.height / 2 + x, y = self.width // 2, self.height // 2 n = 10 self.reticle = pyglet.graphics.vertex_list(4, ('v2i', (x - n, y, x + n, y, x, y - n, x, y + n)) From 0fd908f1cb77ba7408b9a4df0561c3e98284ce7c Mon Sep 17 00:00:00 2001 From: jminardi Date: Wed, 3 Apr 2013 12:48:03 -0500 Subject: [PATCH 28/45] more docstrings --- main.py | 298 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 289 insertions(+), 9 deletions(-) diff --git a/main.py b/main.py index 71fb99d0..a146e0bc 100644 --- a/main.py +++ b/main.py @@ -13,7 +13,8 @@ def cube_vertices(x, y, z, n): - """ Return the vertices of the cube at position x, y, z with size n. + """ Return the vertices of the cube at position x, y, z with size 2*n. + """ return [ x-n,y+n,z-n, x-n,y+n,z+n, x+n,y+n,z+n, x+n,y+n,z-n, # top @@ -27,6 +28,7 @@ def cube_vertices(x, y, z, n): def tex_coord(x, y, n=4): """ Return the bounding vertices of the texture square. + """ m = 1.0 / n dx = x * m @@ -36,6 +38,7 @@ def tex_coord(x, y, n=4): def tex_coords(top, bottom, side): """ Return a list of the texture squares for the top, bottom and side. + """ top = tex_coord(*top) bottom = tex_coord(*bottom) @@ -110,6 +113,7 @@ def __init__(self): self.group = TextureGroup(image.load(TEXTURE_PATH).get_texture()) # A mapping from position to the texture of the block at that position. + # This defines all the blocks that are currently in the world. self.world = {} # Same mapping as `world` but only contains blocks that are shown. @@ -121,12 +125,15 @@ def __init__(self): # Mapping from sector to a list of positions inside that sector. self.sectors = {} - # Simple function queue implemementation. + # Simple function queue implementation. self.queue = [] self._initialize() def _initialize(self): + """ Initialize the world by placing all the blocks. + + """ n = 80 # 1/2 width and height of world s = 1 # step size y = 0 # initial y height @@ -163,7 +170,16 @@ def _initialize(self): def hit_test(self, position, vector, max_distance=8): """ Line of sight search from current position. If a block is intersected it is returned, along with the block previously in the line - of sight. + of sight. If no block is found, return None, None. + + Parameters + ---------- + position : tuple of len 3 + The (x, y, z) position to check visibility from. + vector : tuple of len 3 + The line of sight vector. + max_distance : int + How many blocks away to search for a hit. """ m = 8 @@ -179,6 +195,10 @@ def hit_test(self, position, vector, max_distance=8): return None, None def exposed(self, position): + """ Returns False is given `position` is surrounded on all 6 sides by + blocks, True otherwise. + + """ x, y, z = position for dx, dy, dz in FACES: if (x + dx, y + dy, z + dz) not in self.world: @@ -186,17 +206,32 @@ def exposed(self, position): return False def init_block(self, position, texture): + """ Initialize a block at the given `position` and `texture`, but do + not draw it. + + Parameters + ---------- + position : tuple of len 3 + The (x, y, z) position of the block to initialize. + texture : list of len 3 + The coordinates of the texture squares. Use `tex_coords()` to + generate. + + """ self.add_block(position, texture, False) def add_block(self, position, texture, sync=True): - """ + """ Add a block with the given `texture` and `position` to the world. + Parameters ---------- position : tuple of len 3 - The (x, y, z) position of the block to add - texture : list or len 3 + The (x, y, z) position of the block to add. + texture : list of len 3 The coordinates of the texture squares. Use `tex_coords()` to - generate + generate. + sync : bool + Whether or not to draw the block immediately. """ if position in self.world: @@ -209,6 +244,16 @@ def add_block(self, position, texture, sync=True): self.check_neighbors(position) def remove_block(self, position, sync=True): + """ Remove the block at the given `position`. + + Parameters + ---------- + position : tuple of len 3 + The (x, y, z) position of the block to remove. + sync : bool + Whether or not to immediately remove block from canvas. + + """ del self.world[position] self.sectors[sectorize(position)].remove(position) if sync: @@ -217,6 +262,12 @@ def remove_block(self, position, sync=True): self.check_neighbors(position) def check_neighbors(self, position): + """ Check all blocks surrounding `position` and ensure their visual + state is current. This means hiding blocks that are not exposed and + ensuring that all exposed blocks are shown. Usually used after a block + is added or removed. + + """ x, y, z = position for dx, dy, dz in FACES: key = (x + dx, y + dy, z + dz) @@ -230,11 +281,25 @@ def check_neighbors(self, position): self.hide_block(key) def show_blocks(self): + """ Ensure all exposed blocks are shown. + + """ + # FIXME This method is not currently used for position in self.world: if position not in self.shown and self.exposed(position): self.show_block(position) def show_block(self, position, immediate=True): + """ Show the block at the given `position`. + + Parameters + ---------- + position : tuple of len 3 + The (x, y, z) position of the block to show. + immediate : bool + Whether or not to show the block immediately. + + """ texture = self.world[position] self.shown[position] = texture if immediate: @@ -243,6 +308,17 @@ def show_block(self, position, immediate=True): self._enqueue(self._show_block, position, texture) def _show_block(self, position, texture): + """ Private implementation of the `show_block()` method. + + Parameters + ---------- + position : tuple of len 3 + The (x, y, z) position of the block to show. + texture : list of len 3 + The coordinates of the texture squares. Use `tex_coords()` to + generate. + + """ x, y, z = position # only show exposed faces index = 0 @@ -250,6 +326,7 @@ def _show_block(self, position, texture): vertex_data = cube_vertices(x, y, z, 0.5) texture_data = list(texture) for dx, dy, dz in []: # FACES: + # FIXME This block never gets called. if (x + dx, y + dy, z + dz) in self.world: count -= 4 i = index * 12 @@ -264,6 +341,17 @@ def _show_block(self, position, texture): ('t2f/static', texture_data)) def hide_block(self, position, immediate=True): + """ Hide the block at the given `position`. Hiding does not remove the + block from the world. + + Parameters + ---------- + position : tuple of len 3 + The (x, y, z) position of the block to hide. + immediate : bool + Whether or not to immediately remove the block from the canvas. + + """ self.shown.pop(position) if immediate: self._hide_block(position) @@ -271,19 +359,35 @@ def hide_block(self, position, immediate=True): self._enqueue(self._hide_block, position) def _hide_block(self, position): + """ Private implementation of the 'hide_block()` method. + + """ self._shown.pop(position).delete() def show_sector(self, sector): + """ Ensure all blocks in the given sector that should be shown are + drawn to the canvas. + + """ for position in self.sectors.get(sector, []): if position not in self.shown and self.exposed(position): self.show_block(position, False) def hide_sector(self, sector): + """ Ensure all blocks in the given sector that should be hidden are + removed from the canvas. + + """ for position in self.sectors.get(sector, []): if position in self.shown: self.hide_block(position, False) def change_sectors(self, before, after): + """ Move from sector `before` to sector `after`. A sector is a + contiguous x, y sub-region of world. Sectors are used to speed up + world rendering. + + """ before_set = set() after_set = set() pad = 4 @@ -306,18 +410,30 @@ def change_sectors(self, before, after): self.hide_sector(sector) def _enqueue(self, func, *args): + """ Add `func` to the internal queue. + + """ self.queue.append((func, args)) def _dequeue(self): + """ Pop the top function from the internal queue and call it. + + """ func, args = self.queue.pop(0) func(*args) def process_queue(self): + """ Process the entire queue while taking periodic breaks. + + """ start = time.clock() while self.queue and time.clock() - start < 1 / 60.0: self._dequeue() def process_entire_queue(self): + """ Process the entire queue with no breaks. + + """ while self.queue: self._dequeue() @@ -326,30 +442,71 @@ class Window(pyglet.window.Window): def __init__(self, *args, **kwargs): super(Window, self).__init__(*args, **kwargs) + + # Whether or not the window exclusively captures the mouse. self.exclusive = False + + # When flying gravity has no effect and speed is increased. self.flying = False + + # First element is -1 when moving forward, 1 when moving back, and 0 + # otherwise. The second element is -1 when moving left, 1 when moving + # right, and 0 otherwise. self.strafe = [0, 0] + + # Current (x, y, z) position in the world, specified with floats. self.position = (0, 0, 0) + + # First element is rotation of the player in the x-z plane (ground + # plane) measured from the z-axis down. The second is the rotation + # angle from the ground plane up. self.rotation = (0, 0) + + # Which sector the player is currently in. self.sector = None + + # The crosshairs at the center of the screen. self.reticle = None + + # Velocity in the y (upward) direction. self.dy = 0 + + # A list of blocks the player can place. Hit num keys to cycle. self.inventory = [BRICK, GRASS, SAND] + + # The current block the user can place. Hit num keys to cycle. self.block = self.inventory[0] + + # Convenience list of num keys. self.num_keys = [ key._1, key._2, key._3, key._4, key._5, key._6, key._7, key._8, key._9, key._0] + + # Instance of the model that handles the world. self.model = Model() + + # The label that is displayed in the top left of the canvas. self.label = pyglet.text.Label('', font_name='Arial', font_size=18, x=10, y=self.height - 10, anchor_x='left', anchor_y='top', color=(0, 0, 0, 255)) + + # This call schedules the `update()` method to be called 60 times a + # second. This is the main game event loop. pyglet.clock.schedule_interval(self.update, 1.0 / 60) def set_exclusive_mouse(self, exclusive): + """ If `exclusive` is True, the game will capture the mouse, if False + the game will ignore the mouse. + + """ super(Window, self).set_exclusive_mouse(exclusive) self.exclusive = exclusive def get_sight_vector(self): + """ Returns the current line of sight vector indicating the direction + the player is looking. + + """ x, y = self.rotation m = math.cos(math.radians(y)) dy = math.sin(math.radians(y)) @@ -358,6 +515,15 @@ def get_sight_vector(self): return (dx, dy, dz) def get_motion_vector(self): + """ Returns the current motion vector indicating the velocity of the + player. + + Returns + ------- + vector : tuple of len 3 + Tuple containing the velocity in x, y, and z respectively. + + """ if any(self.strafe): x, y = self.rotation strafe = math.degrees(math.atan2(*self.strafe)) @@ -382,7 +548,14 @@ def get_motion_vector(self): return (dx, dy, dz) def update(self, dt): - """ This method is scheduled to be called repeadetly. + """ This method is scheduled to be called repeatedly by the pyglet + clock. + + Parameters + ---------- + dt : float + The change in time since the last call. + """ self.model.process_queue() sector = sectorize(self.position) @@ -397,6 +570,15 @@ def update(self, dt): self._update(dt / m) def _update(self, dt): + """ Private implementation of the `update()` method. This is where most + of the motion logic lives, along with gravity and collision detection. + + Parameters + ---------- + dt : float + The change in time since the last call. + + """ # walking speed = 15 if self.flying else 5 d = dt * speed @@ -404,7 +586,8 @@ def _update(self, dt): dx, dy, dz = dx * d, dy * d, dz * d # gravity if not self.flying: - self.dy -= dt * 0.044 # g force, should be = jump_speed * 0.5 / max_jump_height + # g force, should be = jump_speed * 0.5 / max_jump_height + self.dy -= dt * 0.044 self.dy = max(self.dy, -0.5) # terminal velocity dy += self.dy # collisions @@ -413,6 +596,22 @@ def _update(self, dt): self.position = (x, y, z) def collide(self, position, height): + """ Checks to see if the player at the given `position` and `height` + is colliding with any blocks in the world. + + Parameters + ---------- + position : tuple of len 3 + The (x, y, z) position to check for collisions at. + height : int or float + The height of the player. + + Returns + ------- + position : tuple of len 3 + The new position of the player taking into account collisions. + + """ pad = 0.25 p = list(position) np = normalize(position) @@ -437,6 +636,10 @@ def collide(self, position, height): return tuple(p) def on_mouse_scroll(self, x, y, scroll_x, scroll_y): + """ Called when the player scrolls the mouse. + + """ + # FIXME This method is not used at all. return x, y, z = self.position dx, dy, dz = self.get_sight_vector() @@ -444,6 +647,22 @@ def on_mouse_scroll(self, x, y, scroll_x, scroll_y): self.position = (x + dx * d, y + dy * d, z + dz * d) def on_mouse_press(self, x, y, button, modifiers): + """ Called when a mouse button is pressed. See pyglet docs for button + amd modifier mappings. + + Parameters + ---------- + x, y : int + The coordinates of the mouse click. Always center of the screen if + the mouse is captured. + button : int + Number representing mouse button that was clicked. 1 = left button, + 4 = right button. + modifiers : int + Number representing any modifying keys that were pressed when the + mouse button was clicked. + + """ if self.exclusive: vector = self.get_sight_vector() block, previous = self.model.hit_test(self.position, vector) @@ -459,6 +678,17 @@ def on_mouse_press(self, x, y, button, modifiers): self.set_exclusive_mouse(True) def on_mouse_motion(self, x, y, dx, dy): + """ Called when the player moves the mouse. + + Parameters + ---------- + x, y : int + The coordinates of the mouse click. Always center of the screen if + the mouse is captured. + dx, dy : float + The movement of the mouse. + + """ if self.exclusive: m = 0.15 x, y = self.rotation @@ -467,6 +697,17 @@ def on_mouse_motion(self, x, y, dx, dy): self.rotation = (x, y) def on_key_press(self, symbol, modifiers): + """ Called when the player presses a key. See pyglet docs for key + mappings. + + Parameters + ---------- + symbol : int + Number representing the key that was pressed. + modifiers : int + Number representing any modifying keys that were pressed. + + """ if symbol == key.W: self.strafe[0] -= 1 elif symbol == key.S: @@ -487,6 +728,17 @@ def on_key_press(self, symbol, modifiers): self.block = self.inventory[index] def on_key_release(self, symbol, modifiers): + """ Called when the player releases a key. See pyglet docs for key + mappings. + + Parameters + ---------- + symbol : int + Number representing the key that was pressed. + modifiers : int + Number representing any modifying keys that were pressed. + + """ if symbol == key.W: self.strafe[0] += 1 elif symbol == key.S: @@ -497,6 +749,9 @@ def on_key_release(self, symbol, modifiers): self.strafe[1] -= 1 def on_resize(self, width, height): + """ Called when the window is resized to a new `width` and `height`. + + """ # label self.label.y = height - 10 # reticle @@ -509,6 +764,9 @@ def on_resize(self, width, height): ) def set_2d(self): + """ Configure OpenGL to draw in 2d. + + """ width, height = self.get_size() glDisable(GL_DEPTH_TEST) glViewport(0, 0, width, height) @@ -519,6 +777,9 @@ def set_2d(self): glLoadIdentity() def set_3d(self): + """ Configure OpenGL to draw in 3d. + + """ width, height = self.get_size() glEnable(GL_DEPTH_TEST) glViewport(0, 0, width, height) @@ -534,6 +795,9 @@ def set_3d(self): glTranslatef(-x, -y, -z) def on_draw(self): + """ Called by pyglet to draw the canvas. + + """ self.clear() self.set_3d() glColor3d(1, 1, 1) @@ -544,6 +808,10 @@ def on_draw(self): self.draw_reticle() def draw_focused_block(self): + """ Draw black edges around the block that is currently under the + crosshairs. + + """ vector = self.get_sight_vector() block = self.model.hit_test(self.position, vector)[0] if block: @@ -555,6 +823,9 @@ def draw_focused_block(self): glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) def draw_label(self): + """ Draw the label in the top left of the screen. + + """ x, y, z = self.position self.label.text = '%02d (%.2f, %.2f, %.2f) %d / %d' % ( pyglet.clock.get_fps(), x, y, z, @@ -562,11 +833,17 @@ def draw_label(self): self.label.draw() def draw_reticle(self): + """ Draw the crosshairs in the center of the screen. + + """ glColor3d(0, 0, 0) self.reticle.draw(GL_LINES) def setup_fog(): + """ Configure the OpenGL fog properties. + + """ glEnable(GL_FOG) glFogfv(GL_FOG_COLOR, (GLfloat * 4)(0.5, 0.69, 1.0, 1)) glHint(GL_FOG_HINT, GL_DONT_CARE) @@ -577,6 +854,9 @@ def setup_fog(): def setup(): + """ Basic OpenGL configuration. + + """ glClearColor(0.5, 0.69, 1.0, 1) glEnable(GL_CULL_FACE) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) From f2e7549fef6e3b0f5875711d47b376643eebd176 Mon Sep 17 00:00:00 2001 From: jminardi Date: Fri, 5 Apr 2013 01:05:57 -0500 Subject: [PATCH 29/45] add more documentation --- main.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/main.py b/main.py index df276de8..cab27a3d 100644 --- a/main.py +++ b/main.py @@ -125,7 +125,8 @@ def __init__(self): # Mapping from sector to a list of positions inside that sector. self.sectors = {} - # Simple function queue implementation. + # Simple function queue implementation. The queue is populated with + # _show_block() and _hide_block() calls self.queue = [] self._initialize() @@ -150,11 +151,13 @@ def _initialize(self): # generate the hills randomly o = n - 10 for _ in xrange(120): - a = random.randint(-o, o) - b = random.randint(-o, o) - c = -1 - h = random.randint(1, 6) - s = random.randint(4, 8) + a = random.randint(-o, o) # x position of the hill + b = random.randint(-o, o) # z position of the hill + c = -1 # base of the hill + h = random.randint(1, 6) # height of the hill + h = 60 + s = random.randint(4, 8) # 2 * s is the side length of the hill + s = 30 d = 1 t = random.choice([GRASS, SAND, BRICK]) for y in xrange(c, c + h): @@ -165,7 +168,7 @@ def _initialize(self): if (x - 0) ** 2 + (z - 0) ** 2 < 5 ** 2: continue self.init_block((x, y, z), t) - s -= d + s -= d # decrement side lenth so hills taper off def hit_test(self, position, vector, max_distance=8): """ Line of sight search from current position. If a block is @@ -290,7 +293,8 @@ def show_blocks(self): self.show_block(position) def show_block(self, position, immediate=True): - """ Show the block at the given `position`. + """ Show the block at the given `position`. This method assumes the + block has already been added with add_block() Parameters ---------- @@ -336,6 +340,7 @@ def _show_block(self, position, texture): else: index += 1 # create vertex list + # FIXME Maybe `add_indexed()` should be used instead self._shown[position] = self.batch.add(count, GL_QUADS, self.group, ('v3f/static', vertex_data), ('t2f/static', texture_data)) @@ -423,7 +428,10 @@ def _dequeue(self): func(*args) def process_queue(self): - """ Process the entire queue while taking periodic breaks. + """ Process the entire queue while taking periodic breaks. This allows + the game loop to run smoothly. The queue contains calls to + _show_block() and _hide_block() so this method should be called if + add_block() or remove_block() was called with sync=False """ start = time.clock() From 1f9a8f7745560f170bd38f9ccdfd20828a34561c Mon Sep 17 00:00:00 2001 From: jminardi Date: Fri, 5 Apr 2013 01:22:31 -0500 Subject: [PATCH 30/45] removed unused code --- main.py | 39 ++------------------------------------- 1 file changed, 2 insertions(+), 37 deletions(-) diff --git a/main.py b/main.py index cab27a3d..7dc4a5f3 100644 --- a/main.py +++ b/main.py @@ -155,10 +155,8 @@ def _initialize(self): b = random.randint(-o, o) # z position of the hill c = -1 # base of the hill h = random.randint(1, 6) # height of the hill - h = 60 s = random.randint(4, 8) # 2 * s is the side length of the hill - s = 30 - d = 1 + d = 1 # how quickly to taper off the hills t = random.choice([GRASS, SAND, BRICK]) for y in xrange(c, c + h): for x in xrange(a - s, a + s + 1): @@ -283,15 +281,6 @@ def check_neighbors(self, position): if key in self.shown: self.hide_block(key) - def show_blocks(self): - """ Ensure all exposed blocks are shown. - - """ - # FIXME This method is not currently used - for position in self.world: - if position not in self.shown and self.exposed(position): - self.show_block(position) - def show_block(self, position, immediate=True): """ Show the block at the given `position`. This method assumes the block has already been added with add_block() @@ -324,24 +313,11 @@ def _show_block(self, position, texture): """ x, y, z = position - # only show exposed faces - index = 0 - count = 24 vertex_data = cube_vertices(x, y, z, 0.5) texture_data = list(texture) - for dx, dy, dz in []: # FACES: - # FIXME This block never gets called. - if (x + dx, y + dy, z + dz) in self.world: - count -= 4 - i = index * 12 - j = index * 8 - del vertex_data[i:i + 12] - del texture_data[j:j + 8] - else: - index += 1 # create vertex list # FIXME Maybe `add_indexed()` should be used instead - self._shown[position] = self.batch.add(count, GL_QUADS, self.group, + self._shown[position] = self.batch.add(24, GL_QUADS, self.group, ('v3f/static', vertex_data), ('t2f/static', texture_data)) @@ -643,17 +619,6 @@ def collide(self, position, height): break return tuple(p) - def on_mouse_scroll(self, x, y, scroll_x, scroll_y): - """ Called when the player scrolls the mouse. - - """ - # FIXME This method is not used at all. - return - x, y, z = self.position - dx, dy, dz = self.get_sight_vector() - d = scroll_y * 10 - self.position = (x + dx * d, y + dy * d, z + dz * d) - def on_mouse_press(self, x, y, button, modifiers): """ Called when a mouse button is pressed. See pyglet docs for button amd modifier mappings. From 9adeeb887151560a9e3663c1e03bd1ebf210b6ae Mon Sep 17 00:00:00 2001 From: jminardi Date: Fri, 5 Apr 2013 01:47:07 -0500 Subject: [PATCH 31/45] change kwargs to be more self consistent --- main.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/main.py b/main.py index 7dc4a5f3..4b7a7a0a 100644 --- a/main.py +++ b/main.py @@ -221,7 +221,7 @@ def init_block(self, position, texture): """ self.add_block(position, texture, False) - def add_block(self, position, texture, sync=True): + def add_block(self, position, texture, immediate=True): """ Add a block with the given `texture` and `position` to the world. Parameters @@ -231,33 +231,33 @@ def add_block(self, position, texture, sync=True): texture : list of len 3 The coordinates of the texture squares. Use `tex_coords()` to generate. - sync : bool + immediate : bool Whether or not to draw the block immediately. """ if position in self.world: - self.remove_block(position, sync) + self.remove_block(position, immediate) self.world[position] = texture self.sectors.setdefault(sectorize(position), []).append(position) - if sync: + if immediate: if self.exposed(position): self.show_block(position) self.check_neighbors(position) - def remove_block(self, position, sync=True): + def remove_block(self, position, immediate=True): """ Remove the block at the given `position`. Parameters ---------- position : tuple of len 3 The (x, y, z) position of the block to remove. - sync : bool + immediate : bool Whether or not to immediately remove block from canvas. """ del self.world[position] self.sectors[sectorize(position)].remove(position) - if sync: + if immediate: if position in self.shown: self.hide_block(position) self.check_neighbors(position) @@ -407,7 +407,7 @@ def process_queue(self): """ Process the entire queue while taking periodic breaks. This allows the game loop to run smoothly. The queue contains calls to _show_block() and _hide_block() so this method should be called if - add_block() or remove_block() was called with sync=False + add_block() or remove_block() was called with immediate=False """ start = time.clock() From f0686c6c06ea0198bb212d40deb06e1dee26446f Mon Sep 17 00:00:00 2001 From: jminardi Date: Fri, 5 Apr 2013 01:55:21 -0500 Subject: [PATCH 32/45] remove init_block() method --- main.py | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/main.py b/main.py index 4b7a7a0a..08ed8c83 100644 --- a/main.py +++ b/main.py @@ -141,12 +141,12 @@ def _initialize(self): for x in xrange(-n, n + 1, s): for z in xrange(-n, n + 1, s): # create a layer stone an grass everywhere. - self.init_block((x, y - 2, z), GRASS) - self.init_block((x, y - 3, z), STONE) + self.add_block((x, y - 2, z), GRASS, immediate=False) + self.add_block((x, y - 3, z), STONE, immediate=False) if x in (-n, n) or z in (-n, n): # create outer walls. for dy in xrange(-2, 3): - self.init_block((x, y + dy, z), STONE) + self.add_block((x, y + dy, z), STONE, immediate=False) # generate the hills randomly o = n - 10 @@ -165,7 +165,7 @@ def _initialize(self): continue if (x - 0) ** 2 + (z - 0) ** 2 < 5 ** 2: continue - self.init_block((x, y, z), t) + self.add_block((x, y, z), t, immediate=False) s -= d # decrement side lenth so hills taper off def hit_test(self, position, vector, max_distance=8): @@ -206,21 +206,6 @@ def exposed(self, position): return True return False - def init_block(self, position, texture): - """ Initialize a block at the given `position` and `texture`, but do - not draw it. - - Parameters - ---------- - position : tuple of len 3 - The (x, y, z) position of the block to initialize. - texture : list of len 3 - The coordinates of the texture squares. Use `tex_coords()` to - generate. - - """ - self.add_block(position, texture, False) - def add_block(self, position, texture, immediate=True): """ Add a block with the given `texture` and `position` to the world. From 87d3b48097470362f01519a525531e8781478007 Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Sat, 6 Apr 2013 19:53:31 -0400 Subject: [PATCH 33/45] use deque --- main.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index 08ed8c83..7cf52084 100644 --- a/main.py +++ b/main.py @@ -2,6 +2,7 @@ import random import time +from collections import deque from pyglet import image from pyglet.gl import * from pyglet.graphics import TextureGroup @@ -127,7 +128,7 @@ def __init__(self): # Simple function queue implementation. The queue is populated with # _show_block() and _hide_block() calls - self.queue = [] + self.queue = deque() self._initialize() @@ -385,7 +386,7 @@ def _dequeue(self): """ Pop the top function from the internal queue and call it. """ - func, args = self.queue.pop(0) + func, args = self.queue.popleft() func(*args) def process_queue(self): From ae4c32109353a26fdfa81e61fd804d1aeb2d35e6 Mon Sep 17 00:00:00 2001 From: Jessica McKellar Date: Tue, 16 Apr 2013 21:46:32 -0400 Subject: [PATCH 34/45] Document setup work. --- main.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/main.py b/main.py index 7cf52084..bd9084dc 100644 --- a/main.py +++ b/main.py @@ -803,11 +803,19 @@ def setup_fog(): """ Configure the OpenGL fog properties. """ + # Enable fog. Fog "blends a fog color with each rasterized pixel fragment's + # post-texturing color." glEnable(GL_FOG) + # Set the fog color. glFogfv(GL_FOG_COLOR, (GLfloat * 4)(0.5, 0.69, 1.0, 1)) + # Say we have no preference between rendering speed and quality. glHint(GL_FOG_HINT, GL_DONT_CARE) + # Specify the equation used to compute the blending factor. glFogi(GL_FOG_MODE, GL_LINEAR) + # Set the fog density. glFogf(GL_FOG_DENSITY, 0.35) + # How close and far away fog starts and ends. The closer the start and end, + # the denser the fog in the fog range. glFogf(GL_FOG_START, 20.0) glFogf(GL_FOG_END, 60.0) @@ -816,8 +824,16 @@ def setup(): """ Basic OpenGL configuration. """ + # Set the color of "clear", i.e. the sky, in rgba. glClearColor(0.5, 0.69, 1.0, 1) + # Enable culling (not rendering) of back-facing facets -- facets that aren't + # visible to you. glEnable(GL_CULL_FACE) + # Set the texture minification/magnification function to GL_NEAREST (nearest + # in Manhattan distance) to the specified texture coordinates. GL_NEAREST + # "is generally faster than GL_LINEAR, but it can produce textured images + # with sharper edges because the transition between texture elements is not + # as smooth." glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) setup_fog() @@ -825,6 +841,7 @@ def setup(): def main(): window = Window(width=800, height=600, caption='Pyglet', resizable=True) + # Hide the mouse cursor and prevent the mouse from leaving the window. window.set_exclusive_mouse(True) setup() pyglet.app.run() From 8737ad9039f0d0dd5cd62978dbfaf305ac85f6a1 Mon Sep 17 00:00:00 2001 From: Jessica McKellar Date: Sat, 27 Apr 2013 11:26:54 -0400 Subject: [PATCH 35/45] Remove GL_FOG_DENSITY, which is unused when using the GL_LINEAR fog mode. --- main.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/main.py b/main.py index bd9084dc..4214c7c9 100644 --- a/main.py +++ b/main.py @@ -812,8 +812,6 @@ def setup_fog(): glHint(GL_FOG_HINT, GL_DONT_CARE) # Specify the equation used to compute the blending factor. glFogi(GL_FOG_MODE, GL_LINEAR) - # Set the fog density. - glFogf(GL_FOG_DENSITY, 0.35) # How close and far away fog starts and ends. The closer the start and end, # the denser the fog in the fog range. glFogf(GL_FOG_START, 20.0) From e6cc7efe40a591ed79b98684313832ef1cce6d86 Mon Sep 17 00:00:00 2001 From: Jessica McKellar Date: Sat, 27 Apr 2013 17:32:44 -0400 Subject: [PATCH 36/45] Document motion and line of sight calculations. --- main.py | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/main.py b/main.py index 4214c7c9..ceb4be6e 100644 --- a/main.py +++ b/main.py @@ -419,17 +419,24 @@ def __init__(self, *args, **kwargs): # When flying gravity has no effect and speed is increased. self.flying = False + # Strafing is moving lateral to the direction you are facing, + # e.g. moving to the left or right while continuing to face forward. + # # First element is -1 when moving forward, 1 when moving back, and 0 # otherwise. The second element is -1 when moving left, 1 when moving # right, and 0 otherwise. self.strafe = [0, 0] - # Current (x, y, z) position in the world, specified with floats. + # Current (x, y, z) position in the world, specified with floats. Note + # that, perhaps unlike in math class, the y-axis is the vertical axis. self.position = (0, 0, 0) # First element is rotation of the player in the x-z plane (ground # plane) measured from the z-axis down. The second is the rotation - # angle from the ground plane up. + # angle from the ground plane up. Rotation is in degrees. + # + # The vertical plane rotation ranges from -90 (looking straight down) to + # 90 (looking straight up). The horizontal rotation range is unbounded. self.rotation = (0, 0) # Which sector the player is currently in. @@ -478,7 +485,12 @@ def get_sight_vector(self): """ x, y = self.rotation + # y ranges from -90 to 90, or -pi/2 to pi/2, so m ranges from 0 to 1 and + # is 1 when looking ahead parallel to the ground and 0 when looking + # straight up or down. m = math.cos(math.radians(y)) + # dy ranges from -1 to 1 and is -1 when looking straight down and 1 when + # looking straight up. dy = math.sin(math.radians(y)) dx = math.cos(math.radians(x - 90)) * m dz = math.sin(math.radians(x - 90)) * m @@ -497,20 +509,26 @@ def get_motion_vector(self): if any(self.strafe): x, y = self.rotation strafe = math.degrees(math.atan2(*self.strafe)) + y_angle = math.radians(y) + x_angle = math.radians(x + strafe) if self.flying: - m = math.cos(math.radians(y)) - dy = math.sin(math.radians(y)) + m = math.cos(y_angle) + dy = math.sin(y_angle) if self.strafe[1]: + # Moving left or right. dy = 0.0 m = 1 if self.strafe[0] > 0: + # Moving backwards. dy *= -1 - dx = math.cos(math.radians(x + strafe)) * m - dz = math.sin(math.radians(x + strafe)) * m + # When you are flying up or down, you have less left and right + # motion. + dx = math.cos(x_angle) * m + dz = math.sin(x_angle) * m else: dy = 0.0 - dx = math.cos(math.radians(x + strafe)) - dz = math.sin(math.radians(x + strafe)) + dx = math.cos(x_angle) + dz = math.sin(x_angle) else: dy = 0.0 dx = 0.0 From c2f602899bf21061d80f273d3bf3d566bc9aa287 Mon Sep 17 00:00:00 2001 From: Jessica McKellar Date: Sat, 27 Apr 2013 19:10:54 -0400 Subject: [PATCH 37/45] Turn magic movement numbers into constants and compute jump speed instead of hard-coding it. --- main.py | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/main.py b/main.py index ceb4be6e..5f2ab3f2 100644 --- a/main.py +++ b/main.py @@ -12,6 +12,20 @@ # Size of sectors used to ease block loading. SECTOR_SIZE = 16 +WALKING_SPEED = 5 +FLYING_SPEED = 15 + +GRAVITY = 20.0 +MAX_JUMP_HEIGHT = 1.0 # About the height of a block. +# To derive the formula for calculating jump speed, first solve +# v_t = v_0 + a * t +# for the time at which you achieve maximum height, where a is the acceleration +# due to gravity and v_t = 0. This gives: +# t = - v_0 / a +# Use t and the desired MAX_JUMP_HEIGHT to solve for v_0 (jump speed) in +# s = s_0 + v_0 * t + (a * t^2) / 2 +JUMP_SPEED = math.sqrt(2 * GRAVITY * MAX_JUMP_HEIGHT) +TERMINAL_VELOCITY = 50 def cube_vertices(x, y, z, n): """ Return the vertices of the cube at position x, y, z with size 2*n. @@ -568,16 +582,19 @@ def _update(self, dt): """ # walking - speed = 15 if self.flying else 5 - d = dt * speed + speed = FLYING_SPEED if self.flying else WALKING_SPEED + d = dt * speed # distance covered this tick. dx, dy, dz = self.get_motion_vector() + # New position in space, before accounting for gravity. dx, dy, dz = dx * d, dy * d, dz * d # gravity if not self.flying: - # g force, should be = jump_speed * 0.5 / max_jump_height - self.dy -= dt * 0.044 - self.dy = max(self.dy, -0.5) # terminal velocity - dy += self.dy + # Update your vertical speed: if you are falling, speed up until you + # hit terminal velocity; if you are jumping, slow down until you + # start falling. + self.dy -= dt * GRAVITY + self.dy = max(self.dy, -TERMINAL_VELOCITY) + dy += self.dy * dt # collisions x, y, z = self.position x, y, z = self.collide((x + dx, y + dy, z + dz), 2) @@ -695,7 +712,7 @@ def on_key_press(self, symbol, modifiers): self.strafe[1] += 1 elif symbol == key.SPACE: if self.dy == 0: - self.dy = 0.016 # jump speed + self.dy = JUMP_SPEED elif symbol == key.ESCAPE: self.set_exclusive_mouse(False) elif symbol == key.TAB: From 6ebbad38939c1bdcf0b84a93edd890d22f2cbd6c Mon Sep 17 00:00:00 2001 From: Jessica McKellar Date: Sat, 27 Apr 2013 23:54:05 -0400 Subject: [PATCH 38/45] Pull out ticks per second into a constant. --- main.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/main.py b/main.py index 5f2ab3f2..c8df2000 100644 --- a/main.py +++ b/main.py @@ -8,6 +8,7 @@ from pyglet.graphics import TextureGroup from pyglet.window import key +TICKS_PER_SEC = 60 # Size of sectors used to ease block loading. SECTOR_SIZE = 16 @@ -411,7 +412,7 @@ def process_queue(self): """ start = time.clock() - while self.queue and time.clock() - start < 1 / 60.0: + while self.queue and time.clock() - start < 1.0 / TICKS_PER_SEC: self._dequeue() def process_entire_queue(self): @@ -481,9 +482,9 @@ def __init__(self, *args, **kwargs): x=10, y=self.height - 10, anchor_x='left', anchor_y='top', color=(0, 0, 0, 255)) - # This call schedules the `update()` method to be called 60 times a - # second. This is the main game event loop. - pyglet.clock.schedule_interval(self.update, 1.0 / 60) + # This call schedules the `update()` method to be called + # TICKS_PER_SEC. This is the main game event loop. + pyglet.clock.schedule_interval(self.update, 1.0 / TICKS_PER_SEC) def set_exclusive_mouse(self, exclusive): """ If `exclusive` is True, the game will capture the mouse, if False From a76ca15fc20e703fa2fcb3e82ede0206795be47b Mon Sep 17 00:00:00 2001 From: Jessica McKellar Date: Sun, 28 Apr 2013 14:25:30 -0400 Subject: [PATCH 39/45] Document collision detection. --- main.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/main.py b/main.py index c8df2000..c9ed9dd1 100644 --- a/main.py +++ b/main.py @@ -28,6 +28,8 @@ JUMP_SPEED = math.sqrt(2 * GRAVITY * MAX_JUMP_HEIGHT) TERMINAL_VELOCITY = 50 +PLAYER_HEIGHT = 2 + def cube_vertices(x, y, z, n): """ Return the vertices of the cube at position x, y, z with size 2*n. @@ -598,7 +600,7 @@ def _update(self, dt): dy += self.dy * dt # collisions x, y, z = self.position - x, y, z = self.collide((x + dx, y + dy, z + dz), 2) + x, y, z = self.collide((x + dx, y + dy, z + dz), PLAYER_HEIGHT) self.position = (x, y, z) def collide(self, position, height): @@ -618,6 +620,10 @@ def collide(self, position, height): The new position of the player taking into account collisions. """ + # How much overlap with a dimension of a surrounding block you need to + # have to count as a collision. If 0, touching terrain at all counts as + # a collision. If .49, you sink into the ground, as if walking through + # tall grass. If >= .5, you'll fall through the ground. pad = 0.25 p = list(position) np = normalize(position) @@ -625,6 +631,7 @@ def collide(self, position, height): for i in xrange(3): # check each dimension independently if not face[i]: continue + # How much overlap you have with this dimension. d = (p[i] - np[i]) * face[i] if d < pad: continue @@ -632,11 +639,12 @@ def collide(self, position, height): op = list(np) op[1] -= dy op[i] += face[i] - op = tuple(op) - if op not in self.model.world: + if tuple(op) not in self.model.world: continue p[i] -= (d - pad) * face[i] if face == (0, -1, 0) or face == (0, 1, 0): + # You are colliding with the ground or ceiling, so stop + # falling / rising. self.dy = 0 break return tuple(p) From eeaaba3d3a1a89989421439a71d04e0da81d8359 Mon Sep 17 00:00:00 2001 From: Jessica McKellar Date: Sun, 28 Apr 2013 14:58:12 -0400 Subject: [PATCH 40/45] Make adding blocks work on OSX. --- main.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/main.py b/main.py index c9ed9dd1..6f962e33 100644 --- a/main.py +++ b/main.py @@ -6,7 +6,7 @@ from pyglet import image from pyglet.gl import * from pyglet.graphics import TextureGroup -from pyglet.window import key +from pyglet.window import key, mouse TICKS_PER_SEC = 60 @@ -669,14 +669,15 @@ def on_mouse_press(self, x, y, button, modifiers): if self.exclusive: vector = self.get_sight_vector() block, previous = self.model.hit_test(self.position, vector) - if button == pyglet.window.mouse.LEFT: - if block: - texture = self.model.world[block] - if texture != STONE: - self.model.remove_block(block) - else: + if (button == mouse.RIGHT) or \ + ((button == mouse.LEFT) and (modifiers & key.MOD_CTRL)): + # ON OSX, control + left click = right click. if previous: self.model.add_block(previous, self.block) + elif button == pyglet.window.mouse.LEFT and block: + texture = self.model.world[block] + if texture != STONE: + self.model.remove_block(block) else: self.set_exclusive_mouse(True) From d02cd72d1ec2ee133b3fec6ac1710e22bea59365 Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Sun, 24 Nov 2013 19:09:08 -0500 Subject: [PATCH 41/45] added link to my other minecraft clone --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index c8267da6..564d4076 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,12 @@ Simple Minecraft-inspired demo written in Python and Pyglet. http://www.youtube.com/watch?v=kC3lwK631X8 +**Like this project?** + +You might also like my other Minecraft clone written in C using modern OpenGL (GL shader language). It performs better, has better terrain generation and saves state to a sqlite database. See here: + +https://github.com/fogleman/Craft + ## Goals and Vision I would like to see this project turn into an educational tool. Kids love Minecraft and Python is a great first language. From a0846148b1372c0587434ead33e25d4adbc453bb Mon Sep 17 00:00:00 2001 From: Rubens Mariuzzo Date: Wed, 27 Jul 2016 16:01:06 -0400 Subject: [PATCH 42/45] =?UTF-8?q?Syntax=20highlighting=20added=20=E2=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 564d4076..9c2113d2 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,14 @@ and see the results quickly. I think it would be great to turn the project into more of a library / API... a Python package that you import and then use / configure to setup a world and run it. Something along these lines... - import mc - - world = mc.World(...) - world.set_block(x, y, z, mc.DIRT) - mc.run(world) + +```python +import mc + +world = mc.World(...) +world.set_block(x, y, z, mc.DIRT) +mc.run(world) +``` The API could contain functionality for the following: @@ -34,26 +37,34 @@ The API could contain functionality for the following: ## How to Run - pip install pyglet - git clone https://github.com/fogleman/Minecraft.git - cd Minecraft - python main.py +```shell +pip install pyglet +git clone https://github.com/fogleman/Minecraft.git +cd Minecraft +python main.py +``` ### Mac On Mac OS X, you may have an issue with running Pyglet in 64-bit mode. Try running Python in 32-bit mode first: - arch -i386 python main.py +```shell +arch -i386 python main.py +``` If that doesn't work, set Python to run in 32-bit mode by default: - defaults write com.apple.versioner.python Prefer-32-Bit -bool yes +```shell +defaults write com.apple.versioner.python Prefer-32-Bit -bool yes +``` This assumes you are using the OS X default Python. Works on Lion 10.7 with the default Python 2.7, and may work on other versions too. Please raise an issue if not. Or try Pyglet 1.2 alpha, which supports 64-bit mode: - pip install https://pyglet.googlecode.com/files/pyglet-1.2alpha1.tar.gz +```shell +pip install https://pyglet.googlecode.com/files/pyglet-1.2alpha1.tar.gz +``` ### If you don't have pip or git From ea8f5e5278df459bf92815fd392ac4b3769b6907 Mon Sep 17 00:00:00 2001 From: OneSharpAce Date: Sat, 16 Feb 2019 17:46:03 -0500 Subject: [PATCH 43/45] Add files via upload Adds support for HiDPI (Retina Macs) --- main.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/main.py b/main.py index 84fc069d..b0590ad9 100644 --- a/main.py +++ b/main.py @@ -779,10 +779,11 @@ def set_2d(self): """ width, height = self.get_size() glDisable(GL_DEPTH_TEST) - glViewport(0, 0, width, height) + viewport = self.get_viewport_size() + glViewport(0, 0, max(1, viewport[0]), max(1, viewport[1])) glMatrixMode(GL_PROJECTION) glLoadIdentity() - glOrtho(0, width, 0, height, -1, 1) + glOrtho(0, max(1, width), 0, max(1, height), -1, 1) glMatrixMode(GL_MODELVIEW) glLoadIdentity() @@ -792,7 +793,8 @@ def set_3d(self): """ width, height = self.get_size() glEnable(GL_DEPTH_TEST) - glViewport(0, 0, width, height) + viewport = self.get_viewport_size() + glViewport(0, 0, max(1, viewport[0]), max(1, viewport[1])) glMatrixMode(GL_PROJECTION) glLoadIdentity() gluPerspective(65.0, width / float(height), 0.1, 60.0) From b0d6b70b1633c615a9102500a46b8079d1619e0e Mon Sep 17 00:00:00 2001 From: marco97pa Date: Mon, 14 Dec 2020 15:37:46 +0100 Subject: [PATCH 44/45] [Fix] time.clock() deprecated in Python 3.8 Replaced all calls to time.clock() with time.perf_counter(). time.clock() has been removed from the time module in Python 3.8. --- main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index b0590ad9..bfdcd32c 100644 --- a/main.py +++ b/main.py @@ -419,8 +419,8 @@ def process_queue(self): add_block() or remove_block() was called with immediate=False """ - start = time.clock() - while self.queue and time.clock() - start < 1.0 / TICKS_PER_SEC: + start = time.perf_counter() + while self.queue and time.perf_counter() - start < 1.0 / TICKS_PER_SEC: self._dequeue() def process_entire_queue(self): From de42fe0c779936da844c960d77fed7e60a742083 Mon Sep 17 00:00:00 2001 From: Tim Gates Date: Wed, 23 Dec 2020 11:55:43 +1100 Subject: [PATCH 45/45] docs: fix simple typo, lenth -> length There is a small typo in main.py. Should read `length` rather than `lenth`. --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index bfdcd32c..6332d97a 100644 --- a/main.py +++ b/main.py @@ -190,7 +190,7 @@ def _initialize(self): if (x - 0) ** 2 + (z - 0) ** 2 < 5 ** 2: continue self.add_block((x, y, z), t, immediate=False) - s -= d # decrement side lenth so hills taper off + s -= d # decrement side length so hills taper off def hit_test(self, position, vector, max_distance=8): """ Line of sight search from current position. If a block is