Reorganize sprite concerns into modules

This commit is contained in:
Alex Clink 2021-11-17 22:12:14 -05:00
parent 521c5108de
commit 4f977a9cf1
11 changed files with 164 additions and 141 deletions

View file

@ -1,10 +0,0 @@
name: LibSDL
ldflags: -lsdl2
packages: sdl2
destdir: src/lib_sdl
definitions:
sdl:
includes:
- SDL2/SDL.h
prefixes:
- sdl

View file

@ -81,4 +81,4 @@ FileUtils.cp "bin/#{BINARY}", "#{BUILD_DIR}/MacOS/#{BINARY}"
puts "Gathering libs..."
gather_libs "#{BUILD_DIR}/MacOS/#{BINARY}"
puts "Done!"
puts "Done!"

View file

@ -1,4 +1,10 @@
class Asteroid < VectorSprite
require "./lx_game/sprite/circle_collision"
require "./lx_game/sprite/vector_sprite"
class Asteroid < Sprite
include LxGame::CircleCollision
include LxGame::VectorSprite
property size : Float64 = 1.0
def initialize
@ -6,10 +12,13 @@ class Asteroid < VectorSprite
@frame = [] of Vector2
end
def update(dt)
update_position(dt)
end
def draw(renderer)
# renderer.draw_color = SDL::Color[0, 100, 100, 255]
# draw_radius(renderer)
frame = project_points(points: @frame, scale: Vector2.new(@size, @size))
renderer.draw_color = SDL::Color[128, 128, 128, 255]
draw_frame(renderer, frame)

View file

@ -1,9 +1,10 @@
class Bullet < Sprite
property age : Float64 = 0.0
require "./lx_game/sprite/age"
def update(dt : Float64)
class Bullet < Sprite
include LxGame::SpriteAge
def update(dt)
update_position(dt)
@age += dt
end
def draw(renderer)
@ -13,6 +14,6 @@ class Bullet < Sprite
end
def collides_with?(other : VectorSprite)
@position.distance(other.position) < other.average_radius
@position.distance(other.position) < other.radius
end
end

View file

@ -1,8 +1,5 @@
class Explosion < LxGame::Emitter
getter age : Float64 = 0.0
require "./lx_game/sprite/age"
def update(dt : Float64)
super
@age += dt
end
class Explosion < LxGame::Emitter
include LxGame::SpriteAge
end

View file

@ -6,14 +6,17 @@ module LxGame
sprite
end
property rotation : Float64
property position : Vector2
property velocity : Vector2
property scale : Vector2
property rotation : Float64
property rotation_speed : Float64
property mass : Float64 = 10.0
def initialize
@position = Vector2.new(0.0, 0.0)
@velocity = Vector2.new(0.0, 0.0)
@scale = Vector2.new(1.0, 1.0)
@rotation = 0.0
@rotation_speed = 0.0
end
@ -23,6 +26,10 @@ module LxGame
@position += @velocity * dt
end
def distance_between(other)
self.position.distance(other.position)
end
abstract def update(dt : Float64)
abstract def draw(renderer : SDL::Renderer)
end

10
src/lx_game/sprite/age.cr Normal file
View file

@ -0,0 +1,10 @@
module LxGame
module SpriteAge
getter age : Float64 = 0.0
def update(dt : Float64)
super
@age += dt
end
end
end

View file

@ -0,0 +1,48 @@
module LxGame
module CircleCollision
property radius : Float64 = 1.0
# Check if two circles are colliding
def collides_with?(other : Sprite)
distance_between(other) < radius + other.radius
end
# Move objects so that they don't overlap
def offset_collision(other : Sprite)
distance = distance_between(other)
overlap = distance - radius - other.radius
offset = ((position - other.position) * (overlap / 2)) / distance
self.position -= offset
other.position += offset
end
# Resolve a collision by offsetting the two positions
# and transfering the momentum
def resolve_collision(other : VectorSprite)
offset_collision(other)
distance = distance_between(other)
# Calculate the new velocities
normal_vec = (position - other.position) / distance
tangental_vec = Vector2.new(-normal_vec.y, normal_vec.x)
# Dot product of velocity with the tangent
# (the direction in which to bounce towards)
dp_tangent_a = velocity.dot(tangental_vec)
dp_tangent_b = other.velocity.dot(tangental_vec)
# Dot product of the normal
dp_normal_a = velocity.dot(normal_vec)
dp_normal_b = other.velocity.dot(normal_vec)
# conservation of momentum
ma = (dp_normal_a * (mass - other.mass) + 2.0 * other.mass * dp_normal_b) / (mass + other.mass)
mb = (dp_normal_b * (other.mass - mass) + 2.0 * mass * dp_normal_a) / (mass + other.mass)
# Set the new velocities
self.velocity = (tangental_vec * dp_tangent_a) + (normal_vec * ma)
other.velocity = (tangental_vec * dp_tangent_b) + (normal_vec * mb)
end
end
end

View file

@ -0,0 +1,71 @@
module LxGame
module VectorSprite
def self.generate_circle(num_points : Int, size = 1.0, jitter = 0.0) : Array(Vector2)
0.upto(num_points).map do |n|
angle = (2 * Math::PI) * (n / num_points)
x = size + rand(-jitter..jitter)
rc = Math.cos(angle)
rs = Math.sin(angle)
Vector2.new(0.0 * rc - x * rs, x * rc + 0.0 * rs)
end.to_a
end
property frame = [] of Vector2
@average_radius : Float64? = nil
def project_points(points : Array(Vector2), rotation = self.rotation, translate : Vector2? = nil, scale : Vector2? = nil)
rc = Math.cos(rotation)
rs = Math.sin(rotation)
translation =
if t = translate
self.position + t
else
self.position
end
points.map do |point|
rotated = Vector2.new(point.x * rc - point.y * rs, point.y * rc + point.x * rs)
scale.try do |scale|
rotated = rotated * scale
end
translation + rotated
end
end
# Calculated as the average R for all points in the frame
def radius
average_radius
end
# Calculated as the average R for all points in the frame
def average_radius
@average_radius ||= begin
# calculate length from center for all points
lengths = frame.map do |vec|
Math.sqrt(vec.x ** 2 + vec.y ** 2)
end
# get the average of the lengths
lengths.reduce { |t, p| t + p } / frame.size.to_f
end
end
def draw_frame(renderer : SDL::Renderer, frame = @frame)
0.upto(frame.size - 1) do |n|
draw_line(renderer, frame[n], frame[(n + 1) % frame.size])
end
end
def draw_radius(renderer : SDL::Renderer, points = 30)
circle = self.class.generate_circle(points, average_radius).map do |point|
point + @position
end
draw_frame(renderer, frame: circle)
end
end
end

View file

@ -1,114 +0,0 @@
module LxGame
abstract class VectorSprite < Sprite
property mass : Float64 = 10.0
property frame = [] of Vector2
@average_radius : Float64? = nil
def self.generate_circle(num_points : Int, size = 1.0, jitter = 0.0) : Array(Vector2)
0.upto(num_points).map do |n|
angle = (2 * Math::PI) * (n / num_points)
x = size + rand(-jitter..jitter)
rc = Math.cos(angle)
rs = Math.sin(angle)
Vector2.new(0.0 * rc - x * rs, x * rc + 0.0 * rs)
end.to_a
end
def project_points(points : Array(Vector2), rotation = @rotation, translate : Vector2? = nil, scale : Vector2? = nil)
rc = Math.cos(rotation)
rs = Math.sin(rotation)
translation =
if t = translate
@position + t
else
@position
end
points.map do |point|
rotated = Vector2.new(point.x * rc - point.y * rs, point.y * rc + point.x * rs)
scale.try do |scale|
rotated = rotated * scale
end
translation + rotated
end
end
def average_radius
@average_radius ||= begin
# calculate length from center for all points
lengths = frame.map do |vec|
Math.sqrt(vec.x ** 2 + vec.y ** 2)
end
# get the average of the lengths
lengths.reduce { |t, p| t + p } / frame.size.to_f
end
end
def update(dt : Float64)
update_position(dt)
end
def draw_frame(renderer : SDL::Renderer, frame = @frame)
0.upto(frame.size - 1) do |n|
draw_line(renderer, frame[n], frame[(n + 1) % frame.size])
end
end
def draw_radius(renderer : SDL::Renderer, points = 30)
circle = self.class.generate_circle(points, average_radius).map do |point|
point + @position
end
draw_frame(renderer, frame: circle)
end
def distance_between(other)
@position.distance(other.position)
end
def collides_with?(other : VectorSprite)
distance_between(other) < average_radius + other.average_radius
end
# Move objects so that they don't overlap
def offset_position(other : VectorSprite)
distance = distance_between(other)
overlap = distance - average_radius - other.average_radius
offset = ((@position - other.position) * (overlap / 2)) / distance
@position -= offset
other.position += offset
end
def resolve_collision(other : VectorSprite)
offset_position(other)
distance = distance_between(other)
# Calculate the new velocities
normal_vec = (@position - other.position) / distance
tangental_vec = Vector2.new(-normal_vec.y, normal_vec.x)
# Dot product of velocity with the tangent
# (the direction in which to bounce towards)
dp_tangent_a = velocity.dot(tangental_vec)
dp_tangent_b = other.velocity.dot(tangental_vec)
# Dot product of the normal
dp_normal_a = velocity.dot(normal_vec)
dp_normal_b = other.velocity.dot(normal_vec)
# conservation of momentum
ma = (dp_normal_a * (mass - other.mass) + 2.0 * other.mass * dp_normal_b) / (mass + other.mass)
mb = (dp_normal_b * (other.mass - mass) + 2.0 * mass * dp_normal_a) / (mass + other.mass)
# Set the new velocities
@velocity = (tangental_vec * dp_tangent_a) + (normal_vec * ma)
other.velocity = (tangental_vec * dp_tangent_b) + (normal_vec * mb)
end
end
end

View file

@ -1,4 +1,8 @@
class Ship < VectorSprite
require "./lx_game/sprite/vector_sprite"
class Ship < Sprite
include LxGame::VectorSprite
@fire_cooldown : Float64 = 0.0
@fire_rate : Float64 = 0.2
@emitter : Emitter
@ -73,7 +77,7 @@ class Ship < VectorSprite
end
def update(dt : Float64)
super
update_position(dt)
@fire_cooldown -= dt unless can_fire?