Skip to content

Instantly share code, notes, and snippets.

@amirrajan
Last active July 30, 2026 14:45
Show Gist options
  • Select an option

  • Save amirrajan/80770d6df09c487e4143c1cb4869e1e1 to your computer and use it in GitHub Desktop.

Select an option

Save amirrajan/80770d6df09c487e4143c1cb4869e1e1 to your computer and use it in GitHub Desktop.
Celeste Classic in DragonRuby Game Toolkit (WIP)
class Game
attr :camera
def initialize world_data
@world_data = world_data
@player = Player.new @world_data.player.ordinal_x,
@world_data.player.ordinal_y
@tiles = @world_data.tiles
@walls = @world_data.walls
@spikes = @world_data.spikes
@camera = {
trauma: 0,
angle: 0,
x_offset: 0,
y_offset: 0
}
end
def tick inputs
@player.tick inputs, @walls
spike = Geometry.find_intersect_rect @player, @spikes, using: :hurt_box
if spike || @player.y < -8
kill_and_reset_player
end
tick_camera
end
def kill_and_reset_player
@camera.trauma = 0.2
@player.reset @world_data.player.ordinal_x,
@world_data.player.ordinal_y
end
def tick_camera
next_camera_angle = 180.0 / 20.0 * @camera.trauma**2
next_offset = 100.0 * @camera.trauma**2
@camera.angle = @camera.angle > 0 ?
next_camera_angle * -1 :
next_camera_angle
@camera.x_offset = next_offset.randomize(:sign, :ratio)
@camera.y_offset = next_offset.randomize(:sign, :ratio)
@camera.trauma *= 0.95
end
def primitives
[
world_primitives,
@player.primitives
]
end
def world_primitives
@tiles.map do |h|
color = if h.type == :wall
{ r: 30, g: 128, b: 255, a: 255 }
elsif h.type == :spike
{ r: 255, g: 128, b: 30, a: 255 }
else
{ r: 0, g: 0, b: 0, a: 0 }
end
{
**h.rect,
path: :solid,
**color
}
end
end
end
class Player
attr :x, :y, :w, :h, :dead
def initialize ordinal_x, ordinal_y
reset ordinal_x, ordinal_y
end
def reset ordinal_x, ordinal_y
@ordinal_x = ordinal_x
@ordinal_y = ordinal_y
@max_dx = 1
@accel = 0.6
@deccel = 0.6
@dx = 0
@dy = 0
@gravity = 0.105
@max_dy = 2
@x = @ordinal_x * 8
@y = @ordinal_y * 8
@on_ground = true
@on_ground_grace = 6
@w = 8
@h = 8
end
def hurt_box
{
x: @x,
y: @y,
w: 8,
h: 8,
}
end
def rect
{
x: @x,
y: @y,
w: 8,
h: 8,
}
end
def tick inputs, walls
if inputs.keyboard.key_down.j && @on_ground
@on_ground = false
@dy = 2
end
if inputs.left_right == 0
@dx = @dx.lerp(0, @deccel)
else
@dx = @dx.lerp(@max_dx * inputs.left_right, @accel)
end
@x += @dx
collision = Geometry.find_intersect_rect self, walls, using: :rect
if collision
if @dx > 0
@x = collision.rect.x - @w
elsif @dx < 0
@x = collision.rect.x + collision.rect.w
end
end
@y += @dy
@dy -= @gravity
collision = Geometry.find_intersect_rect self, walls, using: :rect
if collision
if @dy > 0
@y = collision.rect.y - @h
elsif @dy < 0
@y = collision.rect.y + collision.rect.h
end
@on_ground = true
@on_ground_grace = 6
@dy = 0
elsif @on_ground
next_player_location = {
rect: { x: @x, y: @y - 4, w: 8, h: 8 }
}
collision = Geometry.find_intersect_rect next_player_location, walls, using: :rect
if !collision
@on_ground_grace -= 1
if @on_ground_grace < 0
@on_ground = false
end
end
end
end
def primitives
{
**rect,
path: :solid,
r: 30, g: 255, b: 128
}
end
end
module Main
def start
@game = Game.new parse_world_data
end
def tick
@game.tick inputs
outputs.background_color = [30, 30, 30]
outputs[:scene].background_color = [30, 30, 30]
outputs[:scene].primitives << @game.primitives
outputs.primitives << {
x: 0 - @game.camera.x_offset,
y: 0 - @game.camera.y_offset,
w: 128,
h: 128,
path: :scene,
angle: @game.camera.angle
}
end
def parse_world_data
tiles = []
walls = []
spikes = []
player = nil
ascii_world = <<~S
0000000000000 0
0000000 0
0000 0
000 000
000 0000
000 00
00000 0
00 0
0 0
0 000
0 00
0 00 00
0P 00 00 XXX00
00000 00XX00000
00000XX000000000
0000000000000000
S
symbols = ascii_world.strip
.split("\n")
.map
.with_index do |line, row|
line.chars
.map
.with_index do |char, col|
if char == "0"
:wall
elsif char == "X"
:spike
elsif char == "P"
:player
else
:empty
end
end
end.reverse
symbols.each_with_index do |xs, y|
xs.each_with_index do |t, x|
if t == :player
player = { ordinal_x: x, ordinal_y: y }
elsif t != :empty
entry = {
ordinal_x: x,
ordinal_y: y,
type: t,
rect: {
x: x * 8,
y: y * 8,
w: 8,
h: 8
},
hurt_box: {
x: x * 8 + 1,
y: y * 8 + 1,
w: 6,
h: 6
}
}
tiles << entry
if t == :wall
walls << entry
elsif t == :spike
spikes << entry
end
end
end
end
{
player: player,
tiles: tiles,
walls: walls,
spikes: spikes
}
end
end
DR.reset
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment