mirror of
https://github.com/SleepingInsomniac/asteroids
synced 2024-11-15 19:48:28 +01:00
Reorganize sprite concerns into modules
This commit is contained in:
parent
521c5108de
commit
4f977a9cf1
11 changed files with 164 additions and 141 deletions
10
lib-sdl.yaml
10
lib-sdl.yaml
|
@ -1,10 +0,0 @@
|
|||
name: LibSDL
|
||||
ldflags: -lsdl2
|
||||
packages: sdl2
|
||||
destdir: src/lib_sdl
|
||||
definitions:
|
||||
sdl:
|
||||
includes:
|
||||
- SDL2/SDL.h
|
||||
prefixes:
|
||||
- sdl
|
|
@ -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!"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
10
src/lx_game/sprite/age.cr
Normal file
|
@ -0,0 +1,10 @@
|
|||
module LxGame
|
||||
module SpriteAge
|
||||
getter age : Float64 = 0.0
|
||||
|
||||
def update(dt : Float64)
|
||||
super
|
||||
@age += dt
|
||||
end
|
||||
end
|
||||
end
|
48
src/lx_game/sprite/circle_collision.cr
Normal file
48
src/lx_game/sprite/circle_collision.cr
Normal 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
|
71
src/lx_game/sprite/vector_sprite.cr
Normal file
71
src/lx_game/sprite/vector_sprite.cr
Normal 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
|
|
@ -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
|
|
@ -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?
|
||||
|
||||
|
|
Loading…
Reference in a new issue