diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..1f48ff55 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ + +.vscode/settings.json +*.pyc +*.log diff --git a/README.md b/README.md index 9c2113d2..61f55475 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ 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: diff --git a/codes/__init__.py b/codes/__init__.py new file mode 100644 index 00000000..50d87b4d --- /dev/null +++ b/codes/__init__.py @@ -0,0 +1,6 @@ +# import file +from codes import lib + +# import folders +from codes import blocks +from codes import updates \ No newline at end of file diff --git a/codes/blocks/__init__.py b/codes/blocks/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/codes/blocks/dirt.py b/codes/blocks/dirt.py new file mode 100644 index 00000000..fd585de1 --- /dev/null +++ b/codes/blocks/dirt.py @@ -0,0 +1,8 @@ + + +def ben_update(): + return + + +def ben_random_tick(): + return diff --git a/codes/blocks/grass_block.py b/codes/blocks/grass_block.py new file mode 100644 index 00000000..fd585de1 --- /dev/null +++ b/codes/blocks/grass_block.py @@ -0,0 +1,8 @@ + + +def ben_update(): + return + + +def ben_random_tick(): + return diff --git a/codes/blocks/sand.py b/codes/blocks/sand.py new file mode 100644 index 00000000..fd585de1 --- /dev/null +++ b/codes/blocks/sand.py @@ -0,0 +1,8 @@ + + +def ben_update(): + return + + +def ben_random_tick(): + return diff --git a/codes/blocks/textures.json b/codes/blocks/textures.json new file mode 100644 index 00000000..a675253e --- /dev/null +++ b/codes/blocks/textures.json @@ -0,0 +1,11 @@ +{ + "textures" : { + "blocks" : { + "grass_block" : { + "top" : "grass_block_side", + "side" : "grass_block_top", + "buttom" : "dirt" + } + } + } +} \ No newline at end of file diff --git a/codes/lib.py b/codes/lib.py new file mode 100644 index 00000000..9d38b14b --- /dev/null +++ b/codes/lib.py @@ -0,0 +1,15 @@ +from __future__ import division + +import sys +import math +import random +import time +import os +import re + +from collections import deque +from pyglet import image +# from pyglet.gl import * +from pyglet.graphics import TextureGroup +from pyglet.window import key, mouse + diff --git a/codes/updates/__init__.py b/codes/updates/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/codes/updates/block.py b/codes/updates/block.py new file mode 100644 index 00000000..3eb2d92a --- /dev/null +++ b/codes/updates/block.py @@ -0,0 +1,16 @@ + + +def place_block_update(position, world): + """ + position : tuple of len 3 + The (x, y, z) position of the ben place block to update. + """ + return + + +def remove_block_update(position, world): + """ + position : tuple of len 3 + The (x, y, z) position of the ben remove block to update. + """ + return diff --git a/config.json b/config.json new file mode 100644 index 00000000..43a3c3c0 --- /dev/null +++ b/config.json @@ -0,0 +1,3 @@ +{ + "Minecraft_PE config" +} \ No newline at end of file diff --git a/doc/README-cn.md b/doc/README-cn.md new file mode 100644 index 00000000..44dcf680 --- /dev/null +++ b/doc/README-cn.md @@ -0,0 +1,120 @@ +[English]() + +# 此文档为机翻+部分手动修改,有建议请发中文issue并标明[README-cn] + +# Minecraft + +以Python和Pyglet编写的受Minecraft启发的简单演示。 + +http://www.youtube.com/watch?v=kC3lwK631X8 + +这是皮申杰克元杰 + +**像这个项目?** + +您可能也喜欢我的另一个Minecraft克隆,使用现代OpenGL(GL着色器语言)用C编写。它的性能更好,有更好的地形生成,并将状态保存到sqlite数据库中。请看这里: + +https://github.com/fogleman/Craft + +## 目标和愿景 + +我希望这个项目变成一个教育工具。孩子们喜欢Minecraft,Python是很棒的第一语言。 + +这是一个让孩子们对编程感到兴奋的好机会。 + +代码应该得到很好的注释,并且更容易配置。做一些简单的改变应该很容易 + +很快就能看到结果。 + +我认为把这个项目变成一个库/API会很棒。。。导入一个Python包,然后 + +使用/configure设置一个世界并运行它。沿着这些线索。。。 + +```python +import mc + +world = mc.World(...) +world.set_block(x, y, z, mc.DIRT) +mc.run(world) +``` + +API可以包含以下功能: + +-容易配置的参数,如重力,跳跃速度,步行速度等。 + +-地形生成挂钩。 + +##怎么跑 + +```shell +pip install pyglet +git clone https://github.com/fogleman/Minecraft.git +cd Minecraft +python main.py +``` + +### Mac + +在Mac OS X上,在64位模式下运行Pyglet可能有问题。请先尝试在32位模式下运行Python: + +```shell +arch -i386 python main.py +``` + +如果不起作用,请将Python默认设置为以32位模式运行: + +```shell +defaults write com.apple.versioner.python Prefer-32-Bit -bool yes +``` + +这假设您使用的是OSX默认Python。可以在Lion10.7上使用默认的Python2.7,也可以在其他版本上使用。如果没有,请提出问题。 + +或者试试Pyglet 1.2 alpha,它支持64位模式: + +```shell +pip install https://pyglet.googlecode.com/files/pyglet-1.2alpha1.tar.gz +``` + +### 如果你没有pip或git + +对于pip: + +-在Linux软件包中安装类似于Linux的pip或者类似于pip的软件包。 + +-Windows:[安装分发然后Pip](http://stackoverflow.com/a/12476379/992887)使用链接的.MSI安装程序。 + +对于git: + +-Mac:安装[自制](homebrew网站http://mxhub.com/)首先,然后是“brew install git”。 + +-Windows或Linux:请参阅[安装Git](http://git-scm.com/book/en/Getting-Started-Installing-git)从你的专业博客上。 + +[见维基](https://github.com/fogleman/Minecraft/wiki)为这个项目安装Python等提示。 + +## 怎么玩 + +### 移动 + +-W:前进 +-S:后退 +-A:向左 +-D:向右 + +-鼠标移动:看看周围 +-空格:跳跃 +-Tab:切换飞行模式 + +### 建筑 + +-选择要创建的块类型: + -1:砖 + -2:草 + -3:沙子 + +-鼠标左键单击:删除块 + +-鼠标右键单击:创建块 + +### 退出 + +-释放鼠标,然后关闭窗口 diff --git a/doc/want changes/self.World{}.md b/doc/want changes/self.World{}.md new file mode 100644 index 00000000..e80e98fa --- /dev/null +++ b/doc/want changes/self.World{}.md @@ -0,0 +1,38 @@ + +# now form of self.World + +``` +self.World = { + [x, y, z]: TEXTURE + } +``` + +## TEXTURE are: + +``` +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)) +STONE = tex_coords((2, 1), (2, 1), (2, 1)) +``` + +# the form want to be of self.World + +``` +self.World = { + [chunk_id]: { + [x1, y1, z1]: + [block_id, block_states, [x2, y2 z2]] + } + } +``` + +## inputs + +``` +x/y/z 1 -> the poi in the world +x/y/z 2 -> the poi in the subchunk +block_id : grass block/brick/stone···· +block_states : sand:[float/on ground] +chunk_id : [0, 0]/[0, 1] +``` diff --git a/main.py b/main.py index b0590ad9..8f3253f5 100644 --- a/main.py +++ b/main.py @@ -4,23 +4,41 @@ import math import random import time +import logging +import codes from collections import deque from pyglet import image from pyglet.gl import * from pyglet.graphics import TextureGroup from pyglet.window import key, mouse +from codes.lib import * -TICKS_PER_SEC = 60 - -# Size of sectors used to ease block loading. -SECTOR_SIZE = 16 +# get time when the prgram to start the logging +start_time = time.time() +start_time_date = time.strftime("%Y-%m-%d %H-%M-%S", time.gmtime(start_time)) +# ---------- option start ---------- +log_level = logging.DEBUG # log level +log_file_name = ".\\log\\"+start_time_date + ".log" +# minecraft/log/2020-11-07 12-23-22 +TICKS_PER_SEC = 60 # FPS WALKING_SPEED = 5 FLYING_SPEED = 15 +# ---------- option end ---------- + +# MAINLOG.started +logging.basicConfig(filename=log_file_name, level=log_level, + format="[%(asctime)s][%(name)s]:[%(levelname)s] %(message)s", datefmt="%Y%m%d %H:%M:%S") +MAINLOG = logging.getLogger("main logger") +MAINLOG.info("Minecraft Python Edition Started ") +MAINLOG.info("running on python vision " + sys.version[:5]) +# Size of sectors used to ease block loading. +SECTOR_SIZE = 16 GRAVITY = 20.0 -MAX_JUMP_HEIGHT = 1.0 # About the height of a block. +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 @@ -36,28 +54,7 @@ if sys.version_info[0] >= 3: xrange = range -def cube_vertices(x, y, z, 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 - 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): - """ Return the bounding vertices of the texture square. - - """ - m = 1.0 / n - dx = x * m - dy = y * m - return dx, dy, dx + m, dy, dx + m, dy + m, dx, dy + m +TEXTURE_PATH = 'texture.png' def tex_coords(top, bottom, side): @@ -74,23 +71,91 @@ 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)) STONE = tex_coords((2, 1), (2, 1), (2, 1)) FACES = [ - ( 0, 1, 0), - ( 0,-1, 0), + (0, 1, 0), + (0, -1, 0), (-1, 0, 0), - ( 1, 0, 0), - ( 0, 0, 1), - ( 0, 0,-1), + (1, 0, 0), + (0, 0, 1), + (0, 0, -1), ] +def cube_vertices(x, y, z, 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 + 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): + """ Return the bounding vertices of the texture square. + + """ + m = 1.0 / n + dx = x * m + dy = y * m + return dx, dy, dx + m, dy, dx + m, dy + m, dx, dy + m + + + + +def get_TEXTURES(main_path): + png_re = re.compile(r'.*\\.png') + folder_re = re.compile(r'.*\\..*') + main_lists = os.listdir(main_path) + png_list = [] + folder_list = [] + for dir in main_lists(): + if png_re.match(dir): + png_list.append(dir) + elif not(folder_re.match(dir)): + folder_list.append(dir) + return + + +def folder_open(path, open_all=False, re_match=None): + """folder opener by shenjackyuanjie + Parameters + ---------- + path : str of path + + open_all : bool open every folder? + + re_match : re.compile do match? + + Returns + ------- + list of what you want + """ + main_lists = os.listdir(path) + get_items = [] + for dir in main_lists(): + if (re_match) and (re_match.match(dir)): + get_items.append(dir) + continue + if (open_all) and (not(re.search(r'.*\\..*', dir))): + print(path, dir) + open_path = path.join('\\', dir) + print(open_path) + folder_open(open_path, open_all=True, re_match=re_match) + else: + pass + return get_items + + def normalize(position): """ Accepts `position` of arbitrary precision and returns the block containing that position. @@ -101,9 +166,7 @@ def normalize(position): Returns ------- - block_position : tuple of ints of len 3 - - """ + 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) @@ -118,9 +181,7 @@ def sectorize(position): Returns ------- - sector : tuple of len 3 - - """ + 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) @@ -156,9 +217,7 @@ def __init__(self): self._initialize() def _initialize(self): - """ Initialize the world by placing all the blocks. - - """ + """ 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 @@ -204,9 +263,7 @@ def hit_test(self, position, vector, max_distance=8): vector : tuple of len 3 The line of sight vector. max_distance : int - How many blocks away to search for a hit. - - """ + How many blocks away to search for a hit.""" m = 8 x, y, z = position dx, dy, dz = vector @@ -221,9 +278,7 @@ def hit_test(self, position, vector, max_distance=8): def exposed(self, position): """ Returns False is given `position` is surrounded on all 6 sides by - blocks, True otherwise. - - """ + blocks, True otherwise.""" x, y, z = position for dx, dy, dz in FACES: if (x + dx, y + dy, z + dz) not in self.world: @@ -241,9 +296,7 @@ def add_block(self, position, texture, immediate=True): The coordinates of the texture squares. Use `tex_coords()` to generate. immediate : bool - Whether or not to draw the block immediately. - - """ + Whether or not to draw the block immediately.""" if position in self.world: self.remove_block(position, immediate) self.world[position] = texture @@ -261,9 +314,7 @@ def remove_block(self, position, immediate=True): position : tuple of len 3 The (x, y, z) position of the block to remove. immediate : bool - Whether or not to immediately remove block from canvas. - - """ + Whether or not to immediately remove block from canvas.""" del self.world[position] self.sectors[sectorize(position)].remove(position) if immediate: @@ -275,9 +326,7 @@ 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. - - """ + is added or removed.""" x, y, z = position for dx, dy, dz in FACES: key = (x + dx, y + dy, z + dz) @@ -318,17 +367,15 @@ def _show_block(self, position, texture): 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. - - """ + generate.""" x, y, z = position vertex_data = cube_vertices(x, y, z, 0.5) texture_data = list(texture) # create vertex list # FIXME Maybe `add_indexed()` should be used instead self._shown[position] = self.batch.add(24, GL_QUADS, self.group, - ('v3f/static', vertex_data), - ('t2f/static', texture_data)) + ('v3f/static', vertex_data), + ('t2f/static', texture_data)) def hide_block(self, position, immediate=True): """ Hide the block at the given `position`. Hiding does not remove the @@ -419,9 +466,14 @@ 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: - self._dequeue() + try: + start = time.perf_counter() + while self.queue and time.perf_counter() - start < 1.0 / TICKS_PER_SEC: + self._dequeue() + except: + start = time.clock() + while self.queue and time.clock() - start < 1.0 / TICKS_PER_SEC: + self._dequeue() def process_entire_queue(self): """ Process the entire queue with no breaks. @@ -487,8 +539,8 @@ def __init__(self, *args, **kwargs): # 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)) + 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 # TICKS_PER_SEC. This is the main game event loop. @@ -592,7 +644,7 @@ def _update(self, dt): """ # walking speed = FLYING_SPEED if self.flying else WALKING_SPEED - d = dt * speed # distance covered this tick. + 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 @@ -770,8 +822,9 @@ def on_resize(self, width, height): 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)) - ) + ('v2i', (x - n, y, x + n, + y, x, y - n, x, y + n)) + ) def set_2d(self): """ Configure OpenGL to draw in 2d. @@ -867,8 +920,13 @@ def setup_fog(): glFogi(GL_FOG_MODE, GL_LINEAR) # 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) + fog_start = 40.0 + fog_end = 60.0 + glFogf(GL_FOG_START, fog_start) + glFogf(GL_FOG_END, fog_end) + MAINLOG.debug("fog will start at %f" % (fog_start)) + MAINLOG.debug("fog will end at %f" % (fog_end)) + MAINLOG.info("fog setup finish") def setup(): @@ -887,14 +945,19 @@ def setup(): # as smooth." glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) + MAINLOG.debug("some openGL setup done") setup_fog() def main(): - window = Window(width=800, height=600, caption='Pyglet', resizable=True) + window = Window(width=800, height=600, + caption='minecraft PE', resizable=True) + MAINLOG.info('start up the main window!') # Hide the mouse cursor and prevent the mouse from leaving the window. window.set_exclusive_mouse(True) + MAINLOG.debug('lock the mouse') setup() + MAINLOG.info('setup finish!') pyglet.app.run() diff --git a/textures/minecraft_py/blocks/bedrock.png b/textures/minecraft_py/blocks/bedrock.png new file mode 100644 index 00000000..ba5bf33e Binary files /dev/null and b/textures/minecraft_py/blocks/bedrock.png differ diff --git a/textures/minecraft_py/blocks/bricks.png b/textures/minecraft_py/blocks/bricks.png new file mode 100644 index 00000000..f8d02c91 Binary files /dev/null and b/textures/minecraft_py/blocks/bricks.png differ diff --git a/textures/minecraft_py/blocks/dirt.png b/textures/minecraft_py/blocks/dirt.png new file mode 100644 index 00000000..ba36d70d Binary files /dev/null and b/textures/minecraft_py/blocks/dirt.png differ diff --git a/textures/minecraft_py/blocks/grass_block_side.png b/textures/minecraft_py/blocks/grass_block_side.png new file mode 100644 index 00000000..74ac6653 Binary files /dev/null and b/textures/minecraft_py/blocks/grass_block_side.png differ diff --git a/textures/minecraft_py/blocks/grass_block_top.png b/textures/minecraft_py/blocks/grass_block_top.png new file mode 100644 index 00000000..77a3dc03 Binary files /dev/null and b/textures/minecraft_py/blocks/grass_block_top.png differ diff --git a/textures/minecraft_py/blocks/sand.png b/textures/minecraft_py/blocks/sand.png new file mode 100644 index 00000000..17a62737 Binary files /dev/null and b/textures/minecraft_py/blocks/sand.png differ diff --git a/textures/minecraft_py/texture.png b/textures/minecraft_py/texture.png new file mode 100644 index 00000000..9d05a7e2 Binary files /dev/null and b/textures/minecraft_py/texture.png differ