Skip to content

Instantly share code, notes, and snippets.

@amirrajan
Last active June 3, 2025 22:57
Show Gist options
  • Save amirrajan/70c9c9cd156fb2bc9866268bd1890077 to your computer and use it in GitHub Desktop.
Save amirrajan/70c9c9cd156fb2bc9866268bd1890077 to your computer and use it in GitHub Desktop.
camera with rotation matrix and circle based collision
class Game
attr_gtk
def initialize
@player = { x: 30,
y: -16,
w: 32,
h: 32,
dx: 0,
dy: 0,
path: "sprites/square/blue.png",
rotation: 90.to_radians }
@terrain = 20.map do
{ x: 32 * Numeric.rand(-10..10) - 16,
y: 32 * Numeric.rand(-10..10) - 16,
w: 32,
h: 32,
path: "sprites/square/green.png",}
end
@terrain.each do |t|
t.hit_circ = { x: t.x + t.w / 2,
y: t.y + t.h / 2,
radius: (t.w / 2) * 1.5 }
end
@terrain.reject! { |t| Geometry.intersect_circle?(player_hit_circ, t.hit_circ) }
@camera = { x: 0, y: 0, scale: 1, rotation: 0 }
end
def player_hit_circ
{ x: @player.x + @player.w / 2,
y: @player.y + @player.h / 2,
radius: @player.w / 2 }
end
def calc
4.times do
@player.dx = (inputs.directional_vector&.y || 0) * 5 * Math.cos(@player.rotation) -
(inputs.directional_vector&.x || 0) * 5 * Math.cos(@player.rotation + Math::PI / 2)
@player.dx *= 0.25
@player.x += @player.dx
collision = @terrain.find do |t|
Geometry.intersect_circle?(player_hit_circ, t.hit_circ)
end
if collision
@player.x -= @player.dx
end
end
4.times do
@player.dy = (inputs.directional_vector&.y || 0) * 5 * Math.sin(@player.rotation) -
(inputs.directional_vector&.x || 0) * 5 * Math.sin(@player.rotation + Math::PI / 2)
@player.dy *= 0.25
@player.y += @player.dy
collision = @terrain.find do |t|
Geometry.intersect_circle?(player_hit_circ, t.hit_circ)
end
if collision
@player.y -= @player.dy
end
end
if @player.dx != 0 || @player.dy != 0
@camera_space_matrix = nil
end
if inputs.keyboard.plus || inputs.controller_one.right_analog_y_perc > 0.5
@camera.scale *= 1.01
@camera_space_matrix = nil
elsif inputs.keyboard.minus || inputs.controller_one.right_analog_y_perc < -0.5
@camera.scale = @camera.scale / 1.01
@camera_space_matrix = nil
end
if inputs.keyboard.e || inputs.controller_one.right_analog_x_perc > 0.5
@player.rotation -= 0.05
@camera.rotation -= 0.05
@camera_space_matrix = nil
elsif inputs.keyboard.q || inputs.controller_one.right_analog_x_perc < -0.5
@player.rotation += 0.05
@camera.rotation += 0.05
@camera_space_matrix = nil
end
@camera.x = @player.x
@camera.y = @player.y
end
def tick
Grid.origin_center!
calc
render
end
def render
# outputs.watch @camera
# outputs.watch @player
# outputs.watch to_camera_space(@player)
outputs[:scene].w = 1280
outputs[:scene].h = 720
outputs[:scene].background_color = [0, 0, 0]
player_rect = to_camera_space(@player)
player_circ = { x: player_rect.x + player_rect.w / 2,
y: player_rect.y + player_rect.h / 2,
w: player_rect.w,
h: player_rect.h,
path: "sprites/circle/white.png",
anchor_x: 0.5,
anchor_y: 0.5 }
outputs[:scene].primitives << player_rect
outputs[:scene].primitives << @terrain.map do |t|
rect = to_camera_space(t)
r = rect.w / 2
circ = { x: rect.x + rect.w / 2,
y: rect.y + rect.h / 2,
w: r * 2,
h: r * 2,
path: "sprites/circle/white.png",
anchor_x: 0.5,
anchor_y: 0.5 }
if Geometry.intersect_circle?(player_hit_circ, t.hit_circ)
circ.path = "sprites/circle/red.png"
end
rect
end
outputs.primitives << { x: 0,
y: 0,
w: 1280,
h: 720,
anchor_x: 0.5,
anchor_y: 0.5,
path: :scene }
end
def defaults
end
def to_camera_space rect
@camera_space_matrix ||= Matrix.mul(mat3_translate([email protected], [email protected]),
mat3_scale(@camera.scale),
mat3_rotate([email protected]))
projection_bottom_left = Matrix.mul(Matrix.vec3(rect.x, rect.y, 1),
@camera_space_matrix)
projection_top_right = Matrix.mul(Matrix.vec3(rect.x + rect.w, rect.y + rect.h, 1),
@camera_space_matrix)
rect_angle = (rect.rotation || 0).to_degrees
projection_angle = Geometry.angle(projection_bottom_left, projection_top_right)
rect.merge x: projection_bottom_left.x,
y: projection_bottom_left.y,
w: Geometry.distance(projection_bottom_left, projection_top_right),
h: Geometry.distance(projection_bottom_left, projection_top_right),
angle: projection_angle - 45 + rect_angle
end
def mat3_translate x, y
Matrix.mat3 1, 0, x,
0, 1, y,
0, 0, 1
end
def mat3_scale s
Matrix.mat3 s, 0, 0,
0, s, 0,
0, 0, 1
end
def mat3_rotate t
Matrix.mat3 Math.cos(t), -Math.sin(t), 0,
Math.sin(t), Math.cos(t), 0,
0, 0, 1
end
end
def tick args
$game ||= Game.new
$game.args = args
$game.tick
end
def reset args
$game = nil
end
GTK.reset
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment