RedHenDev / ursina_tutorials

MIT License
45 stars 65 forks source link

Less of an issue and more of an addition (maybe?) (Electric Boogaloo) #21

Open ghost opened 1 year ago

ghost commented 1 year ago

EDIT: Just realized this was in a later part, whoops (Different method I suppose?)

I'm on fire today and wanted a better system to generate chunks than to constantly press "g", and that was to generate chunks similar to how Minecraft handles it's chunk generation.

By telling the system to generate only when the player has moved a distance. Note, that this system is not perfected and I would like the swirl engine take into account the players direction (i.e. north, south, east, etc) and swirl from that starting position, I just haven't figured out a way to implement it (and the player move detection really needs to be improved).

(Note: My files are named differently and vise versa for my variables)

main.py

from ursina import *
from ursina.prefabs.first_person_controller import FirstPersonController
from terrain import MeshTerrain
from swirl_chunks import SwirlEngine

mc = Ursina()

window.color = color.rgb(75, 175, 255)

sky = Sky()
sky.color = window.color

window.borderless = False
window.exit_button.visible = False
camera.fov = 90

scene.fog_density = 0.03
scene.fog_color = color.rgb(50, 125, 200)

mouse.locked = True

player = FirstPersonController()
player.gravity = 0
player.cursor.visible = False

g_terrain = MeshTerrain()

g_terrain.gen_terrain() # Give the player a starting platform than to just fall into the void

plr_old_x = player.X
plr_old_z = player.Z

def update():

    global plr_old_x, plr_old_z # Really preferred to not use a global statement but I wasn't sure how else to implement it

    dist_x = floor(plr_old_x - player.x)
    dist_z = floor(plr_old_z - player.z)

    # print(f"x: {dist_x}")
    # print(f"z: {dist_z}")

    if dist_x < -1 or dist_x > 0 or dist_z < -1 or dist_z > 0:
        plr_old_x = player.X
        plr_old_z = player.Z

        g_terrain.gen_terrain()
    else:
        plr_old_x = player.X
        plr_old_z = player.Z

    block_found = False

    step = 2
    height = 1

    x = str(floor(player.x + 0.5))
    y = floor(player.y + 0.5)
    z = str(floor(player.z + 0.5))
    for i in range(-step, step):
        if g_terrain.terrain_dict.get("x" + x + "-y" + str(y + i) + "-z" + z):
            if g_terrain.terrain_dict.get("x" + x + "-y" + str(y + i) + "-z" + z)[0]:
                # print(f"[INFO] Terrain recorded at {x}, {y}, {z}")
                target = y + i + height
                block_found = True

                break

    if block_found:
        player.y = lerp(player.y, target, 6 * time.dt)
    else:
        player.y -= 9.8 * time.dt

mc.run()

swirl_chunks.py

from ursina import Vec2

class SwirlEngine:
    def __init__(self, sub_width):

        self.sub_width = sub_width

        self.run = 1
        self.iteration = 0
        self.iter_limit = 4
        self.count = 0

        self.pos = Vec2(0, 0)
        self.current_dir = 0
        self.dir = [Vec2(0, 1), Vec2(1, 0), Vec2(0, -1), Vec2(-1, 0)]

    def change_dir(self):

        if self.current_dir < 3:
            self.current_dir += 1
        else:
            self.current_dir = 0
            self.iteration += 1

        if self.current_dir < 2:
            self.run = (self.iteration * 2) - 1
        else:
            self.run = (self.iteration * 2)

    def move(self):

        if self.iteration < self.iter_limit:
            if self.count < self.run:
                self.pos.x += self.dir[self.current_dir].x * self.sub_width
                self.pos.y += self.dir[self.current_dir].y * self.sub_width

                self.count += 1
            else:
                self.count = 0
                self.change_dir()

                self.move()

terrain.py

from ursina import Entity, load_model, Mesh, Vec3, Vec2, floor, Vec4
from ursina.shaders import lit_with_shadows_shader

from perlin import Perlin
from swirl_chunks import SwirlEngine

class MeshTerrain:
    def __init__(self):

        self.block = load_model("block.obj")
        self.atlas = "atlas.png"

        self.subsets = []
        self.subset_limit = 512

        self.sub_width = 16 # Must be even
        self.swirl_engine = SwirlEngine(self.sub_width)
        self.current_subset = 0

        self.terrain_dict = {}
        self.p_noise = Perlin()

        for i in range(0, self.subset_limit):
            ent = Entity(model=Mesh(), texture=self.atlas)
            ent.texture_scale *= 64 / ent.texture.width

            self.subsets.append(ent)

    def gen_block(self, x, y, z):

        from random import random

        uu = 8
        uv = 7

        model = self.subsets[self.current_subset].model

        model.vertices.extend([Vec3(x, y, z) + v for v in self.block.vertices])

        # Biome temp hardcoded until I figure how to implement a proper heatmap
        if y > 2:
            self.terrain_dict["x" + str(floor(x)) + "-y" + str(floor(y)) + "-z" + str(floor(z))] = [True, "Cool"] # [Terrain? True | Yes, False | No, Biome Temperature? Warm? Cold? Hot?]

            uu = 8
            uv = 6
        else:
            self.terrain_dict["x" + str(floor(x)) + "-y" + str(floor(y)) + "-z" + str(floor(z))] = [True, "Warm"]

        color = random() - 0.5
        model.colors.extend((Vec4(1-color, 1-color, 1-color, 1),) * len(self.block.vertices))

        model.uvs.extend([Vec2(uu, uv) + u for u in self.block.uvs])

    def gen_terrain(self):

        x = floor(self.swirl_engine.pos.x)
        z = floor(self.swirl_engine.pos.y)

        dist = int(self.sub_width * 0.5)

        for _x in range(-dist, dist):
            for _z in range(-dist, dist):

                n_x, n_z = x + _x, z + _z

                # y = self.p_noise.get_height(n_x, n_z)
                y = floor(self.p_noise.get_height(n_x, n_z))

                if not self.terrain_dict.get("x" + str(n_x) + "-y" + str(y) + "-z" + str(n_z)):
                    self.gen_block(n_x, y, n_z)

        self.subsets[self.current_subset].model.generate()

        if self.current_subset < self.subset_limit - 1:
            self.current_subset += 1
        else:
            self.current_subset = 0

        if self.swirl_engine.iteration == self.swirl_engine.iter_limit:
            self.swirl_engine.iteration = 0
        else:
            self.swirl_engine.move()
RedHenDev commented 1 year ago

Nice -- this is similar to the original intention, though yours a bit more sophisticated (nice!). Note that pressing 'g' is just for development, i.e. we can control turning off terrain-gen as needed, so definitely not a feature of the finished version.

Like the idea about using direction of player movement.