Benutzer-Werkzeuge

Webseiten-Werkzeuge


Seitenleiste

ss20:neg_sourcecode

← Back to project page

Sourcecode

Download the release version here:
neg-world-engine.zip

Or download the newest version from the Git repository:
https://gitlab.tubit.tu-berlin.de/srather/NEG-World-Engine.git

Folder Structure:

Root


README

README.md
# NEG-World-Engine
 
> A 2D Non-Euclidean Engine bases on portals
 
![](icon_large.png)
 
**Dependencies:**
 
- Python 3.7.7 or newer (get it [here](https://www.python.org/downloads/))
- PyGame 1.9.6 or newer (get it via `pip install pygame`)
 
 
**Setup:**
 
- Download the sourcecode ([here](https://gitlab.tubit.tu-berlin.de/srather/NEG-World-Engine.git))
- Unpack the downloaded archive
- execute `main.py`
 
**Controls:**
 
- Move with WASD, the arrow keys or by clicking with your mouse
- Press the number keys 0-9 to explore different worlds

NEG-World-Engine

A 2D Non-Euclidean Engine bases on portals

Dependencies:

  • Python 3.7.7 or newer (get it here)
  • PyGame 1.9.6 or newer (get it via pip install pygame)

Setup:

  • Download the sourcecode (here)
  • Unpack the downloaded archive
  • execute main.py

Controls:

  • Move with WASD, the arrow keys or by clicking with your mouse
  • Press the number keys 0-9 to explore different worlds

↑ Back to top


main

main.py
import pygame
import math
 
import sys
sys.path.append(".\\datatypes")
sys.path.append(".\\drawables")
sys.path.append(".\\utility")
 
import generate
import draw
from vector import *
from polygon import *
from portal import *
from floor import *
 
 
# global variables
WINDOW = Vector(1280, 960)
POSITION = Vector(0, 0)
ZOOM = 25
SPEED = 10
FPS = 70
DEPTH = 0
 
current_fps = FPS
current_speed = Vector(0, 0)
 
 
# generate worlds
def gen4():
    """
    Generate world 4 (and 8).
    Return a big polygon with sides as mirrors and a
    slightly smaller one with polygons on its vertices.
    """
 
    def mirror(m, v):
        v=Vector(*v)
        return 2 * v.dot(m) / m.dot(m) * m - v
    w = []
    pol = generate.polygon(4, (0, 0), 500)
    for i, _ in enumerate(pol):
        w.append(generate.polygon(i+3, 0.5 * pol[i], 50, phase=0.5))
    for i, _ in enumerate(pol):
        w.append(Portal(pol[i-1], pol[i], M=(mirror(pol[i]-pol[i-1],(1,0)), mirror(pol[i]-pol[i-1],(0,1)))))
    return w
 
def gen6():
    """
    Generate additional objects for world 6.
    Return the 2 houses with their doors.
    """
 
    w = []
    w += generate.border(generate.polygon(4, (0, 0), 200, phase=-0.5))[:-1]
    w += generate.border(generate.polygon(4, (1000, 0), 200, phase=-0.5))
    w += generate.door(Vector(0, -100*2**0.5), Vector(-100*2**0.5, -100*2**0.5), 0.5)
    for point in generate.polygon(4, (0, 0), 100*2**0.5):
        d = generate.door(point, Vector(0, 0), 0.5)
        w.append(d[0]); w.append(d[1])
    for point in generate.polygon(4, (1000, 0), 100*2**0.5):
        d = generate.door(point, Vector(1000, 0), 0.5)
        w.append(d[0]); w.append(d[1])
    return w
 
worlds = [
    # 1: short longer tunnel
    [
        generate.polygon(3, (-150, -200), 50),
        Polygon(( 100,-100), (-100,-100)),
        Polygon(( 100,-200), (-100,-200)),
        Portal ((-100,-100), (-100,-200), M=((2.0,0), (0,1))),
        Portal (( 100,-200), ( 100,-100), M=((2.0,0), (0,1))),
    ],
 
    # 2: tilted tunnel
    [
        Polygon(( 200, 100), (-200, 200)),
        Polygon(( 200, 200), (-200, 300)),
        Portal ((-200, 300), (-200, 200), M=((0.5,0.25), (0,1))),
        Portal (( 200, 100), ( 200, 200), M=((0.5,0.25), (0,1))),
    ],
 
    #3: 1 mirror
    [
        generate.star(5, (200, -200), 50, phase=0.5),
        generate.polygon(4, (0, -200), 50),
        generate.polygon(7, (0, 800), 50, phase=0.5),
        Portal((-200, -300), (-200, -100), M=((-1,0),(0,1)), M2=((1,0),(0,1)), offset2=(0, 1000)),
        Portal((-200, 900), (-200, 700), M=((-1,0),(0,1)), M2=((1,0),(0,1)), offset2=(0, -1000)),
    ],
 
    # 4: 4 mirrors
    [
 
    ]
    + gen4(),
 
    # 5: around the pole
    [
        generate.polygon(3, (0, -200), 50, phase=0.5, color=colors.DARK_RED),
        generate.star(5, (2000, -200), 75, phase=0.5, color=colors.DARK_GREEN),
        Portal((0, -50), (1000, -50), offset=(2000, 0), offset2=(2000, 0)),
        Portal((2000, -50), (3000, -50), offset=(-2000, 0), offset2=(-2000, 0)),
    ],
 
    # 6: big room
    [
        Polygon((100*2**0.5, -100*2**0.5), (0, -100*2**0.5)),
        Portal((0, 100*2**0.5), (0, 0), offset=(1000,0), offset2=(1000,0)),
        Portal((1000, 100*2**0.5), (1000, 0), offset=(-1000,0), offset2=(-1000,0)),
        Floor((*generate.polygon(4, (    -100/2**0.5, -100/2**0.5), 99.9, phase=0.5)), color=colors.DARK_RED),
        Floor((*generate.polygon(4, (    -100/2**0.5,  100/2**0.5), 99.9, phase=0.5)), color=colors.DARK_ORANGE),
        Floor((*generate.polygon(4, (1000+100/2**0.5,  100/2**0.5), 99.9, phase=0.5)), color=colors.DARK_YELLOW),
        Floor((*generate.polygon(4, (1000+100/2**0.5, -100/2**0.5), 99.9, phase=0.5)), color=colors.DARK_GREEN),
        Floor((*generate.polygon(4, (1000-100/2**0.5, -100/2**0.5), 99.9, phase=0.5)), color=colors.DARK_CYAN),
        Floor((*generate.polygon(4, (1000-100/2**0.5,  100/2**0.5), 99.9, phase=0.5)), color=colors.DARK_BLUE),
        Floor((*generate.polygon(4, (     100/2**0.5,  100/2**0.5), 99.9, phase=0.5)), color=colors.DARK_PURPLE),
        Floor((*generate.polygon(4, (     100/2**0.5, -100/2**0.5), 99.9, phase=0.5)), color=colors.DARK_MAGENTA),
    ]
    + gen6(),
 
    # 7: shorter/longer tunnel bad fps
    [
        Polygon(( 100,-100), (-100,-100)),
        Polygon(( 100,-200), (-100,-200)),
        Portal ((-100,-100), (-100,-200), M=((2.0,0), (0,1))),
        Portal (( 100,-200), ( 100,-100), M=((2.0,0), (0,1))),
        Polygon(( 200, 100), (-200, 100)),
        Polygon(( 200, 200), (-200, 200)),
        Portal ((-200, 200), (-200, 100), M=((0.5,0), (0,1))),
        Portal (( 200, 100), ( 200, 200), M=((0.5,0), (0,1))),
    ],
 
    # 8: 4 mirors 2 depth
    [
 
    ]
    + gen4(),
 
    # 9: shoter/longer tunnel depth: 0
    [
        Polygon(( 100,-100), (-100,-100)),
        Polygon(( 100,-200), (-100,-200)),
        Portal ((-100,-100), (-100,-200), M=((2.0,0), (0,1))),
        Portal (( 100,-200), ( 100,-100), M=((2.0,0), (0,1))),
        Polygon(( 200, 100), (-200, 100)),
        Polygon(( 200, 200), (-200, 200)),
        Portal ((-200, 200), (-200, 100), M=((0.5,0), (0,1))),
        Portal (( 200, 100), ( 200, 200), M=((0.5,0), (0,1))),
    ],
 
    # 0: random assortment
    [
        Polygon((-30, 0)),
        Polygon((10, 30), (30, 100)),
        Polygon((100, -100), (200, -100)),
        Polygon((-100, -100), (-100, -200), (-200, -100)),
        Polygon((100, 100), (100, 200), (200, 200), (200, 100)),
        Portal((300, -200), (200, -300), M=((-1,0),(0,-1))),
        Portal((-100, 300), (100, 300), M=((1,0),(0,-0.5))),
    ],
 
]
 
 
def bake_physics(wrld):
    """
    Return a list of all lines an points in 'wrld' which have a collision.
    """
 
    lns = []
    for object in wrld:
        # ignore floor tiles
        if type(object) is Floor:
            continue
        # dissasample polygons into lines
        if isinstance(object, Polygon):
            if len(object) <= 2:
                lns.append(object)
                continue
            for i, _ in enumerate(object):
                lns.append(object.__class__(object[i-1], object[i]))
    return lns
 
 
# initialise
pygame.init()
window = pygame.display.set_mode(WINDOW)
pygame.display.set_caption("NEG World Engine")
pygame.display.set_icon(pygame.image.load('icon_small.png'))
clock = pygame.time.Clock()
world = [] # empty starting world
lines = bake_physics(world)
 
 
# main loop
running = True
while running:
    # exit program
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
 
    # draw frame
    window.fill(colors.GRAY)
    draw.world(window, world, POSITION, DEPTH)
    draw.player(window, POSITION, ZOOM)
    draw.debug(window, 0, f"{current_fps:.0f} FPS", colors.BRIGHT_GREEN)
    draw.debug(window, 1, f"{POSITION.x:.0f} X {POSITION.y:.0f} y", colors.BRIGHT_GREEN)
 
    # check inputs
    keys = pygame.key.get_pressed()
 
    # change world if numbers are pressed
    if keys[pygame.K_1]: world = worlds[0]; lines = bake_physics(world); POSITION *= 0; current_speed *= 0; DEPTH = 2
    if keys[pygame.K_2]: world = worlds[1]; lines = bake_physics(world); POSITION *= 0; current_speed *= 0; DEPTH = 2
    if keys[pygame.K_3]: world = worlds[2]; lines = bake_physics(world); POSITION *= 0; current_speed *= 0; DEPTH = 1
    if keys[pygame.K_4]: world = worlds[3]; lines = bake_physics(world); POSITION *= 0; current_speed *= 0; DEPTH = 1
    if keys[pygame.K_5]: world = worlds[4]; lines = bake_physics(world); POSITION *= 0; current_speed *= 0; DEPTH = 1
    if keys[pygame.K_6]: world = worlds[5]; lines = bake_physics(world); POSITION = Vector(0, -200); current_speed *= 0; DEPTH = 1
    if keys[pygame.K_7]: world = worlds[6]; lines = bake_physics(world); POSITION *= 0; current_speed *= 0; DEPTH = 1
    if keys[pygame.K_8]: world = worlds[7]; lines = bake_physics(world); POSITION *= 0; current_speed *= 0; DEPTH = 2
    if keys[pygame.K_9]: world = worlds[8]; lines = bake_physics(world); POSITION *= 0; current_speed *= 0; DEPTH = 0
    if keys[pygame.K_0]: world = worlds[9]; lines = bake_physics(world); POSITION *= 0; current_speed *= 0; DEPTH = 1
 
    # change speed if arrows are pressed
    force = Vector(0, 0)
    if keys[pygame.K_LEFT ] or keys[pygame.K_a]: force += Vector(-1,  0)
    if keys[pygame.K_RIGHT] or keys[pygame.K_d]: force += Vector( 1,  0)
    if keys[pygame.K_UP   ] or keys[pygame.K_w]: force += Vector( 0, -1)
    if keys[pygame.K_DOWN ] or keys[pygame.K_s]: force += Vector( 0,  1)
    force = force.normalised()
 
    # change speed if left mouse button is pressed
    if pygame.mouse.get_focused():
        if pygame.mouse.get_pressed()[0]:
            force += (Vector(*pygame.mouse.get_pos()) - WINDOW / 2).normalised()
 
    # apply changes (not opimal, movement should be independant of framerate)
    current_speed += force.normalised() * SPEED / max(current_fps, 0.1)
    current_speed *= SPEED ** -(1 / max(current_fps, 60))
 
    POSITION += current_speed
 
    # check physics (not optimal, player can glitch into things)
    for line in lines:
        if line.dist_to(POSITION) < ZOOM/2:
            if (POSITION - line[0]).length() < ZOOM/2:
                b_a = (POSITION - current_speed - line[0]).normal()
            elif (POSITION - line[1]).length() < ZOOM/2:
                b_a = (POSITION - current_speed - line[1]).normal()
            elif type(line) is Polygon:
                b_a = line[1] - line[0]
            else:
                continue
 
            # removes the part of the speed vector which causes collision
            POSITION -= current_speed
            current_speed = b_a * (b_a.dot(current_speed) / b_a.dot(b_a))
            POSITION += current_speed
 
    # check if gone through portal
    move = Ray(POSITION - current_speed, current_speed)
    for object in world:
        if type(object) is Portal:
            # intersect portal with movement ray
            port = Ray(object[0], object[1] - object[0])
            point = move.intersect(port)
            if point: # not empty
                if move.value_at(*point) <= 1 and port.value_at(*point) <= 1:
                    # change world to portal world
                    world = object.go_through(move.dir, world)
                    lines = bake_physics(world)
 
    # wait tick and update
    clock.tick(FPS)
    current_fps = clock.get_fps()
    pygame.display.flip()
 
 
# exit routine
pygame.quit()

↑ Back to top

ss20/neg_sourcecode.txt · Zuletzt geändert: 2020/09/11 14:25 von srather