start making procedural level generation

This commit is contained in:
2025-08-24 20:26:07 +10:00
parent c690f309cf
commit d166632f90
8 changed files with 897 additions and 94 deletions

View File

@@ -11,7 +11,7 @@ config_version=5
[application]
config/name="jumpery"
run/main_scene="uid://cfvb33kf48sga"
run/main_scene="uid://1c1ewfy1hxh"
config/use_custom_user_dir=true
config/custom_user_dir_name="jumpery"
config/features=PackedStringArray("4.4", "GL Compatibility")

View File

@@ -49,7 +49,7 @@ physics_interpolation_mode = 2
visible = false
top_level = true
script = ExtResource("6_tuyoq")
_steps = 180
steps = 180
_line_color = Color(1, 1, 0, 1)
_charge_color = Color(0, 1, 1, 1)
_hit_color = Color(0, 1, 1, 1)

File diff suppressed because it is too large Load Diff

54
scenes/test_gen.tscn Normal file
View File

@@ -0,0 +1,54 @@
[gd_scene load_steps=9 format=4 uid="uid://1c1ewfy1hxh"]
[ext_resource type="PackedScene" uid="uid://xlkx300774in" path="res://scenes/debugger.tscn" id="1_co1np"]
[ext_resource type="TileSet" uid="uid://bvg74b201o0a7" path="res://resources/tileset.tres" id="2_p27jg"]
[ext_resource type="PackedScene" uid="uid://db7d5pwp4gds" path="res://scenes/player.tscn" id="2_t6id5"]
[ext_resource type="Script" uid="uid://cww6w0p32k5rm" path="res://scripts/main_camera.gd" id="3_ay1b2"]
[ext_resource type="Script" uid="uid://dhi3fk24ivjb3" path="res://scripts/generator.gd" id="4_ay1b2"]
[sub_resource type="WorldBoundaryShape2D" id="WorldBoundaryShape2D_8kul5"]
normal = Vector2(-1, 0)
[sub_resource type="WorldBoundaryShape2D" id="WorldBoundaryShape2D_t6id5"]
normal = Vector2(1, 0)
[sub_resource type="WorldBoundaryShape2D" id="WorldBoundaryShape2D_co1np"]
[node name="TestGen" type="Node2D"]
[node name="Debugger" parent="." instance=ExtResource("1_co1np")]
[node name="WorldBoundary" type="StaticBody2D" parent="."]
position = Vector2(241, 180)
[node name="WorldBoundaryShapeR" type="CollisionShape2D" parent="WorldBoundary"]
position = Vector2(232, 58)
shape = SubResource("WorldBoundaryShape2D_8kul5")
[node name="WorldBoundaryShapeL" type="CollisionShape2D" parent="WorldBoundary"]
position = Vector2(-232, 48)
shape = SubResource("WorldBoundaryShape2D_t6id5")
[node name="WorldBoundaryShapeB" type="CollisionShape2D" parent="WorldBoundary"]
position = Vector2(0, 140)
shape = SubResource("WorldBoundaryShape2D_co1np")
[node name="TileMapLayer" type="TileMapLayer" parent="."]
position = Vector2(8, 0)
tile_map_data = PackedByteArray("AAD//wAAAQAAAAAAAAD//wEAAQAAAAEAAAD//wIAAQAAAAEAAAD//wMAAQAAAAEAAAD//wQAAQAAAAEAAAD//wUAAQAAAAEAAAD//wYAAQAAAAEAAAD//wcAAQAAAAEAAAD//wgAAQAAAAEAAAD//wkAAQAAAAEAAAD//woAAQAAAAEAAAD//wsAAQAAAAEAAAD//wwAAQAAAAEAAAD//w0AAQAAAAEAAAD//w4AAQAAAAEAAAD//w8AAQAAAAEAAAD//xAAAQAAAAEAAAD//xEAAQAAAAEAAAD//xIAAQAAAAEAAAD//xMAAQAAAAEAAAD//xQAAQAEAAEAAAD//xUAAQAIAAEAAAD//xYAAQAIAAMAAAAdAAAAAQAAAAAAAAAdAAEAAQAAAAEAAAAdAAIAAQAAAAEAAAAdAAMAAQAAAAEAAAAdAAQAAQAAAAEAAAAdAAUAAQAAAAEAAAAdAAYAAQAAAAEAAAAdAAcAAQAAAAEAAAAdAAgAAQAAAAEAAAAdAAkAAQAAAAEAAAAdAAoAAQAAAAEAAAAdAAsAAQAAAAEAAAAdAAwAAQAAAAEAAAAdAA0AAQAAAAEAAAAdAA4AAQAAAAEAAAAdAA8AAQAAAAEAAAAdABAAAQAAAAEAAAAdABEAAQAAAAEAAAAdABIAAQAAAAEAAAAdABMAAQAAAAEAAAAdABQAAQAHAAEAAAAdABUAAQALAAIAAAAdABYAAQALAAMAAAAcABYAAQAJAAMAAAAbABYAAQAJAAMAAAAaABYAAQAJAAMAAAAZABYAAQAJAAMAAAAYABYAAQAJAAMAAAAXABYAAQAJAAMAAAAWABYAAQAJAAMAAAAVABYAAQAJAAMAAAAUABYAAQAJAAMAAAATABYAAQAJAAMAAAASABYAAQAJAAMAAAARABYAAQAJAAMAAAAQABYAAQAJAAMAAAAPABYAAQAJAAMAAAAOABYAAQAJAAMAAAANABYAAQAJAAMAAAAMABYAAQAJAAMAAAALABYAAQAJAAMAAAAKABYAAQAJAAMAAAAKABUAAQAJAAIAAAAJABUAAQAJAAIAAAAIABUAAQAJAAIAAAAHABUAAQAJAAIAAAAGABUAAQAJAAIAAAAFABUAAQAJAAIAAAAEABUAAQAJAAIAAAADABUAAQAJAAIAAAACABUAAQAJAAIAAAABABUAAQAJAAIAAAAAABUAAQAJAAIAAAAAABYAAQAJAAMAAAABABYAAQAJAAMAAAACABYAAQAJAAMAAAADABYAAQAJAAMAAAAEABYAAQAJAAMAAAAFABYAAQAJAAMAAAAGABYAAQAJAAMAAAAHABYAAQAJAAMAAAAIABYAAQAJAAMAAAAJABYAAQAJAAMAAAALABUAAQAJAAIAAAANABUAAQAJAAIAAAAMABUAAQAJAAIAAAAOABUAAQAJAAIAAAAPABUAAQAJAAIAAAAQABUAAQAJAAIAAAARABUAAQAJAAIAAAASABUAAQAJAAIAAAATABUAAQAJAAIAAAAUABUAAQAJAAIAAAAVABUAAQAJAAIAAAAWABUAAQAJAAIAAAAXABUAAQAJAAIAAAAYABUAAQAJAAIAAAAZABUAAQAJAAIAAAAaABUAAQAJAAIAAAAbABUAAQAJAAIAAAAcABUAAQAJAAIAAAAcABQAAQAKAAAAAAAbABQAAQAKAAAAAAAaABQAAQAKAAAAAAAZABQAAQAKAAAAAAAYABQAAQAKAAAAAAAXABQAAQAKAAAAAAAWABQAAQAKAAAAAAAVABQAAQAKAAAAAAAUABQAAQAKAAAAAAATABQAAQAKAAAAAAARABQAAQAKAAAAAAASABQAAQAKAAAAAAAQABQAAQAKAAAAAAAPABQAAQAKAAAAAAAOABQAAQAKAAAAAAANABQAAQAKAAAAAAAMABQAAQAKAAAAAAALABQAAQAKAAAAAAAKABQAAQAKAAAAAAAJABQAAQAKAAAAAAAIABQAAQAKAAAAAAAHABQAAQAKAAAAAAAGABQAAQAKAAAAAAAFABQAAQAKAAAAAAAEABQAAQAKAAAAAAADABQAAQAKAAAAAAACABQAAQAKAAAAAAABABQAAQAKAAAAAAAAABQAAQAKAAAAAAA=")
tile_set = ExtResource("2_p27jg")
[node name="Player" parent="." instance=ExtResource("2_t6id5")]
position = Vector2(42, 320)
[node name="MainCamera" type="Camera2D" parent="."]
position = Vector2(240, 180)
script = ExtResource("3_ay1b2")
[node name="Trajectories" type="Node2D" parent="."]
[node name="Generator" type="Node" parent="." node_paths=PackedStringArray("_tile_map", "_trajectory_holder")]
script = ExtResource("4_ay1b2")
_tile_map = NodePath("../TileMapLayer")
_trajectory_holder = NodePath("../Trajectories")
metadata/_custom_type_script = "uid://dhi3fk24ivjb3"

34
scripts/generator.gd Normal file
View File

@@ -0,0 +1,34 @@
class_name Generator
extends Node
const RANDOM_SEED: int = -1
@export var generation_seed: int = RANDOM_SEED
@export_group("References")
@export var _tile_map: TileMapLayer
@export var _trajectory_holder: Node2D
@onready var _tile_set := _tile_map.tile_set
func _ready() -> void:
if generation_seed != RANDOM_SEED:
seed(generation_seed)
var pos := Player.instance.global_position
for i in range(3):
var trajectory := Trajectory.new()
trajectory.global_position = pos
trajectory.flip_direction = i % 2 != 0
_trajectory_holder.add_child(trajectory)
var result_pos := trajectory.calculate(false)
result_pos.y += 1
var cell_coords := _tile_map.local_to_map(_tile_map.to_local(result_pos))
_tile_map.set_cell(cell_coords, 1, Vector2i(0, 3))
_tile_map.set_cells_terrain_connect([cell_coords], 0, 0)
pos = _tile_map.to_global(_tile_map.map_to_local(cell_coords))
pos.y -= _tile_set.tile_size.y / 2.0

1
scripts/generator.gd.uid Normal file
View File

@@ -0,0 +1 @@
uid://dhi3fk24ivjb3

View File

@@ -306,9 +306,10 @@ class CollisionHandler:
var to := collisions[i] + Vector2.DOWN
var query := PhysicsRayQueryParameters2D.create(from, to)
var result := space_state.intersect_ray(query)
if result["normal"] != Vector2.ZERO:
var normal := result["normal"] as Vector2
angle = -normal.angle_to(Vector2.UP)
if is_zero_approx(angle):
return angle
if result:
if result["normal"] != Vector2.ZERO:
var normal := result["normal"] as Vector2
angle = -normal.angle_to(Vector2.UP)
if is_zero_approx(angle):
return angle
return angle

View File

@@ -90,9 +90,9 @@ const JUMP_REFERENCE: PackedFloat32Array = [
set(value):
charge_strength = value
queue_redraw()
@export_range(0, 200) var _steps: int = 60:
@export_range(0, 200) var steps: int = 60:
set(value):
_steps = value
steps = value
queue_redraw()
@export var _jump: bool = true:
set(value):
@@ -118,7 +118,7 @@ const JUMP_REFERENCE: PackedFloat32Array = [
set(value):
_is_reference = value
queue_redraw()
@export_tool_button("Calculate") var calculate := queue_redraw
@export_tool_button("Recalculate") var recalculate := queue_redraw
@export_group("Metrics")
@export var _player_size: Vector2i = Vector2i(18, 26):
@@ -167,6 +167,10 @@ func _draw() -> void:
if not Engine.is_editor_hint() and Debugger.mode == Debugger.Mode.DISABLED:
return
calculate()
func calculate(drawing: bool = true) -> Vector2:
if not _player_shape:
_player_shape = RectangleShape2D.new()
_player_shape.size = _player_size
@@ -184,18 +188,20 @@ func _draw() -> void:
)
points.append(pos)
draw_polyline(points, Color.WHITE, 2)
return
if drawing:
draw_polyline(points, Color.WHITE, 2)
return to_global(pos)
var charge_strength_line := (
Vector2(sin(charge_strength * PI * 2), -cos(charge_strength * PI * 2)) * 30
)
draw_line(pos, pos + charge_strength_line, _charge_color)
draw_dashed_line(
pos + Vector2.LEFT * _player_size.x / 2,
pos + Vector2.RIGHT * _player_size.x / 2,
Color.WHITE
)
if drawing:
var charge_strength_line := (
Vector2(sin(charge_strength * PI * 2), -cos(charge_strength * PI * 2)) * 30
)
draw_line(pos, pos + charge_strength_line, _charge_color)
draw_dashed_line(
pos + Vector2.LEFT * _player_size.x / 2,
pos + Vector2.RIGHT * _player_size.x / 2,
Color.WHITE
)
var velocity: Vector2
var direction: float = 0
@@ -209,7 +215,7 @@ func _draw() -> void:
var pos_prev := pos
var space_state := get_world_2d().direct_space_state
for i in range(_steps):
for i in range(steps):
if is_on_floor and is_zero_approx(floor_angle) and _stop_on_landing:
break
@@ -265,27 +271,30 @@ func _draw() -> void:
jump_released = handler.jump_released
velocity = handler.velocity
if handler.hit_floor or handler.hit_slope:
_draw_collision_hit(pos, SIDE_BOTTOM, _hit_color)
if handler.hit_ceiling:
_draw_collision_hit(pos, SIDE_TOP, _hit_color)
if handler.hit_wall:
_draw_collision_hit(
pos, SIDE_LEFT if velocity.x > 0 else SIDE_RIGHT, _hit_color
)
if drawing:
if handler.hit_floor or handler.hit_slope:
_draw_collision_hit(pos, SIDE_BOTTOM, _hit_color)
if handler.hit_ceiling:
_draw_collision_hit(pos, SIDE_TOP, _hit_color)
if handler.hit_wall:
_draw_collision_hit(
pos, SIDE_LEFT if velocity.x > 0 else SIDE_RIGHT, _hit_color
)
if i % 10 == 0 && i > 0:
if drawing and i % 10 == 0 and i > 0:
_draw_arrowhead(pos_prev, pos, _line_color)
points.append(pos)
pos_prev = pos
if _draw_hitbox:
if drawing and _draw_hitbox:
draw_rect(
Rect2(_get_hitbox_pos(pos), _player_size), Color(_hitbox_color, 0.1)
)
draw_polyline(points, _line_color)
if drawing:
draw_polyline(points, _line_color)
return to_global(pos)
func _cast_motion(