commit 3fe6eb2dd9ba21783899bbb9fb6ba50346cb478c Author: DuCouscous Date: Mon Mar 6 11:56:55 2023 +0100 osef diff --git a/__pycache__/block.cpython-310.pyc b/__pycache__/block.cpython-310.pyc new file mode 100644 index 0000000..257d829 Binary files /dev/null and b/__pycache__/block.cpython-310.pyc differ diff --git a/__pycache__/chunk.cpython-310.pyc b/__pycache__/chunk.cpython-310.pyc new file mode 100644 index 0000000..d970184 Binary files /dev/null and b/__pycache__/chunk.cpython-310.pyc differ diff --git a/__pycache__/perlin.cpython-310.pyc b/__pycache__/perlin.cpython-310.pyc new file mode 100644 index 0000000..b2b2046 Binary files /dev/null and b/__pycache__/perlin.cpython-310.pyc differ diff --git a/__pycache__/renderer.cpython-310.pyc b/__pycache__/renderer.cpython-310.pyc new file mode 100644 index 0000000..8d17575 Binary files /dev/null and b/__pycache__/renderer.cpython-310.pyc differ diff --git a/__pycache__/world.cpython-310.pyc b/__pycache__/world.cpython-310.pyc new file mode 100644 index 0000000..fc3b939 Binary files /dev/null and b/__pycache__/world.cpython-310.pyc differ diff --git a/block.py b/block.py new file mode 100644 index 0000000..486ad37 --- /dev/null +++ b/block.py @@ -0,0 +1,28 @@ +class BlockType: + NONE = 0 + STONE = 1 + WOOD = 2 + GRASS = 3 + +class Block: + def __init__(self, id: int, durability: int = 0, type: int = BlockType.NONE): + self.id: int = id + self.durability: int = durability + self.type: int = type + + +class BlockAir(Block): + def __init__(self): + super().__init__(0) + +class BlockStone(Block): + def __init__(self): + super().__init__(1, 20, BlockType.STONE) + +class BlockDirt(Block): + def __init__(self): + super().__init__(2, 10, BlockType.GRASS) + +class BlockGrass(Block): + def __init__(self): + super().__init__(3, 10, BlockType.GRASS) \ No newline at end of file diff --git a/chunk.py b/chunk.py new file mode 100644 index 0000000..dc36e25 --- /dev/null +++ b/chunk.py @@ -0,0 +1,20 @@ +import block, perlin + +CHUNK_WIDTH = 32 +CHUNK_HEIGHT = 32 + +class Chunk: + def gen_block(self, x: int, y: int): + height = int(100 + perlin.noise2d(x, 0.1) * 10) + + if y < height: return block.BlockAir() + if y == height: return block.BlockGrass() + if y > height and y < height + 4: return block.BlockDirt() + + return block.BlockStone() + + def get(self, x: int, y: int) -> block.Block(id): + return self.blocks[x + CHUNK_WIDTH * y] + + def __init__(self, x: int, y: int): + self.blocks = [self.gen_block(i % CHUNK_WIDTH + x * CHUNK_WIDTH, i // CHUNK_WIDTH + y * CHUNK_HEIGHT) for i in range(CHUNK_WIDTH * CHUNK_HEIGHT)] diff --git a/main.py b/main.py new file mode 100644 index 0000000..fb703d8 --- /dev/null +++ b/main.py @@ -0,0 +1,3 @@ +from renderer import Renderer + +Renderer() \ No newline at end of file diff --git a/makefile b/makefile new file mode 100644 index 0000000..fe28434 --- /dev/null +++ b/makefile @@ -0,0 +1,2 @@ +all : + python main.py \ No newline at end of file diff --git a/perlin.py b/perlin.py new file mode 100644 index 0000000..9b1029b --- /dev/null +++ b/perlin.py @@ -0,0 +1,65 @@ +import math, random + +W = 255 +H = 255 +background=(100,100,100) +gradtable = [ (0,0) for i in range(0,W*H) ] + +def precalc_gradtable(): + rnd = random.Random() + for i in range(0,H): + for j in range(0, W): + x = float((rnd.randint(1,2*W))-W)/W + y = float((rnd.randint(1,2*H))-H)/H + s = math.sqrt(x * x + y * y) + if s!=0: + x = x / s + y = y / s + else: + x = 0 + y = 0 + gradtable[i*H+j] = (x,y) + +#calculate dot product for v1 and v2 +def dot(v1,v2): + return ( (v1[0]*v2[0]) + (v1[1]*v2[1]) ) + +# get a pseudorandom gradient vector +def gradient(x,y): + + # normalize! + return gradtable[y*H+x] + +def s_curve(x): + return ( 3*x*x - 2*x*x*x ) + +def noise2d(x,y): + + x0 = math.floor(x) + y0 = math.floor(y) + x1 = x0 + 1.0 + y1 = y0 + 1.0 + + i_x0 = int(x0) + i_x1 = int(x1) + i_y0 = int(y0) + i_y1 = int(y1) + + s = dot(gradient(i_x0, i_y0),(x-x0, y-y0)) + t = dot(gradient(i_x1, i_y0),(x-x1, y-y0)) + u = dot(gradient(i_x0, i_y1),(x-x0, y-y1)) + v = dot(gradient(i_x1, i_y1),(x-x1, y-y1)) + + s_x = s_curve( x - x0 ) + a = s + s_x*t - s_x*s + b = u + s_x*v - s_x*u + + s_y = s_curve( y - y0 ) + z = a + s_y*b - s_y*a + + return z + +def col( a ): + return int(round((128-(128*a)))) + +precalc_gradtable() \ No newline at end of file diff --git a/renderer.py b/renderer.py new file mode 100644 index 0000000..5480395 --- /dev/null +++ b/renderer.py @@ -0,0 +1,69 @@ +import tkinter as tk +from PIL import Image,ImageTk +import random +import threading +import time +from world import World + +RENDER_WIDTH, RENDER_HEIGHT = 640 // 32 + 1, 480 // 32 + 1 +FPS = 20 + +class Camera: + def __init__(self, x: int, y: int): + self.x: int = x + self.y: int = y + +class Renderer: + def render(self): + offset_x, offset_y = self.camera.x % 32, self.camera.y % 32 + index = 0 + self.world.update_chunks(self.camera.x // 32, self.camera.y // 32) + for tile in self.tiles: + self.canvas.moveto(tile, 32 * (index % RENDER_WIDTH)- offset_x, 32 * (index // RENDER_WIDTH) - offset_y) + self.canvas.itemconfig(tile, image=self.sprites[self.world.get_block( + self.camera.x // 32 + index % RENDER_WIDTH, + self.camera.y // 32 + index // RENDER_WIDTH + ).id]) + index += 1 + pass + + def update_thread(self): + while self.running: + self.render() + self.camera.x += 8 + time.sleep(1 / FPS) + pass + + def __init__(self): + self.running = True + self.window = tk.Tk() + self.window.title("MC 2D") + self.window.geometry("640x480") + + self.camera = Camera(90 * 32, 90 * 32) + self.world = World(self.camera.x // 32, self.camera.y // 32) + + self.sprites = [ + "sprites/air.png", + "sprites/stone.png", + "sprites/dirt.png", + "sprites/grass.png" + ] + + self.sprites = [ + ImageTk.PhotoImage(Image.open(sprite)) for sprite in self.sprites + ] + + self.canvas = tk.Canvas(self.window, width=640, height=480, background="white") + self.canvas.pack() + + self.tiles = [ + self.canvas.create_image(32 * (i % RENDER_WIDTH) - 16, 32 * (i // RENDER_WIDTH) - 16, anchor="nw", image=self.sprites[random.randint(0, len(self.sprites) - 1)]) + for i in range(RENDER_WIDTH * RENDER_HEIGHT) + ] + + self.tupdate = threading.Thread(target=self.update_thread) + self.tupdate.start() + + self.window.mainloop() + self.running = False \ No newline at end of file diff --git a/sprites/air.png b/sprites/air.png new file mode 100644 index 0000000..e318ab6 Binary files /dev/null and b/sprites/air.png differ diff --git a/sprites/dirt.png b/sprites/dirt.png new file mode 100644 index 0000000..1c38076 Binary files /dev/null and b/sprites/dirt.png differ diff --git a/sprites/grass.png b/sprites/grass.png new file mode 100644 index 0000000..1b5ea95 Binary files /dev/null and b/sprites/grass.png differ diff --git a/sprites/stone.png b/sprites/stone.png new file mode 100644 index 0000000..8c0a8e4 Binary files /dev/null and b/sprites/stone.png differ diff --git a/world.py b/world.py new file mode 100644 index 0000000..a92697f --- /dev/null +++ b/world.py @@ -0,0 +1,29 @@ +from block import Block, BlockAir, BlockStone +import perlin +from chunk import Chunk, CHUNK_HEIGHT, CHUNK_WIDTH + +class World: + def __init__(self, x: int, y: int): + self.chunks = {} + self.old_x = -1 + self.old_y = -1 + self.update_chunks(x, y) + pass + + def update_chunks(self, x: int, y: int): + x //= CHUNK_WIDTH + y //= CHUNK_HEIGHT + if self.old_x == x and self.old_y == y: return + print("Refresh chunks") + + self.chunks = {} + + for i in range(x - 1, x + 2): + for j in range(y - 1, y + 2): + self.chunks[f"{i}:{j}"] = Chunk(i, j) + + self.old_x = x + self.old_y = y + + def get_block(self, x: int, y: int) -> Block: + return self.chunks[f"{x // CHUNK_WIDTH}:{y // CHUNK_HEIGHT}"].get(x % CHUNK_WIDTH, y % CHUNK_HEIGHT)