Skip to content

Commit

Permalink
fix: generate a new chunk at each lap (#42)
Browse files Browse the repository at this point in the history
* refactor(race): clean the race generation script

* fix(race): generate a new chunk at each lap

* ci: better event trigger for release-packaging

* chore add changelog entry
  • Loading branch information
florianvazelle authored Feb 10, 2024
1 parent ee356f7 commit 3a391df
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 52 deletions.
7 changes: 6 additions & 1 deletion .github/workflows/release-packaging.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
name: Release Packaging

on:
push:
workflow_dispatch:
# Ensure the build works on main
push:
branches: [main]
tags:
# Ensure the build works on each pull request
pull_request:

env:
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
### Removed
### Fixed
- Use a custom `EditorExportPlugin` to set build info ([#41](https://github.com/MechanicalFlower/Marble/pull/41))
- Generate a new chunk at each lap ([#42](https://github.com/MechanicalFlower/Marble/pull/42))
### Security
### Dependencies
- Bump `actions/cache` from 3 to 4 ([#40](https://github.com/MechanicalFlower/Marble/pull/40))
Expand Down
32 changes: 20 additions & 12 deletions scripts/main.gd
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@ var _mode: int = State.MODE_START
var _current_marble_index := 0
var _time := 0.0
var _explosion_enabled := false
var _need_new_chunk := false
var _race_has_started := false

# Variables used in explosion mode to check
# if we need to generate another chunk of the race
var _max_checkpoint_count := -1
var _old_lap_count := 0

# There are limited places to ensure equality among the marbles.
# TODO : remove this limit
var _positions := []
Expand Down Expand Up @@ -101,7 +105,7 @@ func _unhandled_input(event):
# Debug command to generate a new race
KEY_R:
if _mode == State.MODE_MARBLE:
_race.call_deferred(&"generate_race", !_explosion_enabled)
_race.generate_race(!_explosion_enabled)

KEY_SPACE:
for marble in _marbles:
Expand Down Expand Up @@ -211,7 +215,7 @@ func set_mode(mode):
await Fade.fade_out(1, Color.BLACK, "Diamond", false, false).finished

_explosion_enabled = SettingsManager.get_value(&"marbles", &"explosion_enabled") as bool
_race.call_deferred(&"generate_race", !_explosion_enabled)
_race.generate_race(!_explosion_enabled)

_overlay.reset()
reset_position()
Expand Down Expand Up @@ -276,7 +280,7 @@ func _process(delta):
if _time > TIME_PERIOD:
if _mode == State.MODE_START:
# Regenerate race
_race.call_deferred(&"generate_race", true)
_race.generate_race(true)

# Reset timer
_time = 0
Expand All @@ -300,14 +304,18 @@ func _process(delta):
_explosion.set_emitting(true)

if _ranking._first_marble:
if (
_need_new_chunk
and (_ranking._first_marble._checkpoint_count + 3) % _race._step_count != 0
):
_race.generate_chunk()
_need_new_chunk = false
elif (_ranking._first_marble._checkpoint_count + 3) % _race._step_count == 0:
_need_new_chunk = true
# If a new checkpoint is crossed by the first marble
if _ranking._first_marble._checkpoint_count > _max_checkpoint_count:
# Store the max number of checkpoints crossed
_max_checkpoint_count = _ranking._first_marble._checkpoint_count

# Compute the lap (1 lap equals to one chunk)
var lap_count: int = ceil((_max_checkpoint_count + 3) / _race._step_count)
# If one more lap was done
if lap_count > _old_lap_count:
# Generate a chunk
_race.generate_chunk()
_old_lap_count = lap_count
else:
_panel_timer.hide()

Expand Down
145 changes: 106 additions & 39 deletions scripts/race.gd
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
@tool

class_name Race
extends Node

var PieceList = load("res://scripts/constants/piece_list.gd")
var Group = load("res://scripts/constants/groups.gd")
# Constants and resource paths
const PieceList = preload("res://scripts/constants/piece_list.gd")
const Group = preload("res://scripts/constants/groups.gd")

@export var regenerate_race: bool:
set = generate_race

# Number of steps in the race generation
var _step_count := 10
var _previous_piece: Piece = null
var _piece_orientation = null

var _previous_piece = null
var _previous_piece_orientation = null
var _previous_rotation_index := 0
var _curve: Curve3D

# Curve to store the race path
@onready var curve: Curve3D = Curve3D.new()

# Reference to the path node
@onready var path := get_node(^"Path") as Path3D


Expand All @@ -22,79 +30,138 @@ static func umod(x: int, d: int) -> int:


func _ready() -> void:
_curve = Curve3D.new()
path.set_curve(_curve)
# Initialize the curve and set it to the path
path.set_curve(curve)

# Check if explosion is enabled from settings
var explosion_enabled = SettingsManager.get_value(&"marbles", &"explosion_enabled") as bool
generate_race(!explosion_enabled)


# Main function to generate the race
func generate_race(with_end: bool = true) -> void:
_curve.clear_points()
# Clear previous race path
curve.clear_points()
# Clear previous pieces from the scene
clear_previous_pieces()
# Place the start line
place_start_line()

for piece in get_children():
if piece.is_in_group(Group.PIECES):
piece.queue_free()
# Generate a race chunk
generate_chunk()

# Place the finish line if required
if with_end:
place_finish_line()


# Function to generate a race chunk
func generate_chunk() -> void:
randomize()

# Reset values
for _i in range(_step_count):
# Select a random piece and place it
var piece_index = randomize_piece_index()
place_piece(piece_index)


# Function to clear previous pieces from the scene
func clear_previous_pieces() -> void:
_previous_piece = null
_piece_orientation = null
_previous_piece_orientation = null
_previous_rotation_index = 0

# Place the start line
place_piece(-2)
for piece in get_children():
if piece.is_in_group(Group.PIECES):
piece.queue_free()

generate_chunk()

if with_end:
# Place the finish line
place_piece(-1)
# Function to select a random piece index
func randomize_piece_index() -> int:
return umod(randi(), len(PieceList.PIECES) - 2)


func generate_chunk():
for step in _step_count:
# Select a random piece
var piece_index = umod(randi(), len(PieceList.PIECES) - 2)
place_piece(piece_index)
# Function to place the start line
func place_start_line() -> void:
place_piece(-2)


# Function to place the finish line
func place_finish_line() -> void:
place_piece(-1)


# Function to place a piece on the race path
func place_piece(piece_index: int) -> void:
# Instantiate the piece
var piece_data = PieceList.PIECES[piece_index]
var piece: Node = piece_data[&"resource"].instantiate()
var piece: Piece = piece_data[&"resource"].instantiate()

# Optimize piece visibility
optimize_piece_visibility(piece)

# naive hlod
# Add the piece to the scene
add_child(piece)

# Rotate and translate the piece
rotate_piece(piece)
translate_piece(piece)

# Store data for next piece and positions for the race path
store_piece_data(piece, piece_data)
store_piece_positions(piece)


# Function to optimize piece visibility
func optimize_piece_visibility(piece: Piece) -> void:
for child in piece.get_children():
if child is MeshInstance3D:
child.visibility_range_end = 150

# Add the piece to the main Node
add_child(piece)

# Rotate the piece
# Function to rotate a piece based on previous orientation
func rotate_piece(piece: Piece) -> void:
var rotation_index = calculate_rotation_index(piece)
piece.rotate_y(float(rotation_index) * PI / 2.0)


# Function to calculate the rotation index for a piece
func calculate_rotation_index(_piece: Piece) -> int:
var rotation_index = _previous_rotation_index
if _piece_orientation == PieceList.TURN_LEFT:
if _previous_piece_orientation == PieceList.TURN_LEFT:
rotation_index = (rotation_index + 1) % 4
elif _piece_orientation == PieceList.TURN_RIGHT:
elif _previous_piece_orientation == PieceList.TURN_RIGHT:
rotation_index = (rotation_index - 1) % 4
piece.rotate_y(float(rotation_index) * PI / 2.0)
return rotation_index


# Translate the piece
# Function to translate a piece based on previous piece position
func translate_piece(piece: Piece) -> void:
var offset = calculate_translation_offset(piece)
piece.global_translate(offset)


# Function to calculate the translation offset for a piece
func calculate_translation_offset(piece: Piece) -> Vector3:
var offset = Vector3(-10, 25, 0)
if _previous_piece != null:
offset = (
_previous_piece.get_end().global_transform.origin
- piece.get_begin().global_transform.origin
)
piece.global_translate(offset)
return offset

# Store data for next piece

# Function to store data for the next piece
func store_piece_data(piece: Piece, piece_data: Dictionary) -> void:
_previous_piece = piece
_piece_orientation = piece_data[&"next_piece_orientation"]
_previous_rotation_index = rotation_index
_previous_rotation_index = calculate_rotation_index(piece)
_previous_piece_orientation = piece_data[&"next_piece_orientation"]


# Function to store positions for the race path
func store_piece_positions(piece: Piece) -> void:
var positions := piece.get_node(^"Positions") as Marker3D
if positions:
for pos in positions.get_children():
_curve.add_point(pos.global_position)
curve.add_point(pos.global_position)

0 comments on commit 3a391df

Please sign in to comment.